653 lines
16 KiB
Markdown
653 lines
16 KiB
Markdown
# Looking - Backend API
|
|
|
|
> **Express.js REST API with MongoDB for the Looking art project**
|
|
|
|
This is the backend REST API that powers the "Looking" application. Built with Express 4 and MongoDB 4.4, it provides endpoints for profile management, user authentication, messaging, and geolocation features.
|
|
|
|
---
|
|
|
|
## 📋 Table of Contents
|
|
|
|
- [Overview](#overview)
|
|
- [Tech Stack](#tech-stack)
|
|
- [Project Structure](#project-structure)
|
|
- [Getting Started](#getting-started)
|
|
- [Data Flow](#data-flow)
|
|
- [Environment Variables](#environment-variables)
|
|
- [Database Seeding](#database-seeding)
|
|
- [Development Workflow](#development-workflow)
|
|
- [Troubleshooting](#troubleshooting)
|
|
- [Related Documentation](#related-documentation)
|
|
|
|
---
|
|
|
|
## 🎯 Overview
|
|
|
|
The backend API provides:
|
|
|
|
- **Authentication**: JWT-based user sessions with PBKDF2 password hashing
|
|
- **Profile Management**: CRUD operations with approval workflow
|
|
- **Messaging**: Conversation threads with image support
|
|
- **Image Processing**: Automatic thumbnail and detail image generation
|
|
- **Geolocation**: Google Maps API integration for location features
|
|
- **Email**: Password reset and notification emails via Nodemailer
|
|
|
|
**Server Port**: 3069 (configurable via PORT environment variable)
|
|
**Database**: MongoDB 4.4 on port 27017 (database name: `urge`)
|
|
|
|
---
|
|
|
|
## 🛠 Tech Stack
|
|
|
|
| Technology | Version | Purpose |
|
|
| ---------------- | ------- | ---------------------- |
|
|
| **Express** | 4.14.0 | Web framework |
|
|
| **Node.js** | 14.x | Runtime |
|
|
| **MongoDB** | 4.4 | Database |
|
|
| **Mongoose** | 4.7.4 | MongoDB ODM |
|
|
| **jsonwebtoken** | 7.3.0 | JWT authentication |
|
|
| **multer** | 1.2.0 | File upload handling |
|
|
| **@google/maps** | 0.4.5 | Google Maps API client |
|
|
| **nodemailer** | 4.0.1 | Email sending |
|
|
| **winston** | 2.4.0 | Application logging |
|
|
| **morgan** | 1.7.0 | HTTP request logging |
|
|
| **moment** | 2.17.1 | Date/time manipulation |
|
|
| **shortid** | 2.2.8 | Short ID generation |
|
|
| **vcard-js** | 1.2.2 | VCard generation |
|
|
|
|
**Dev Tools:**
|
|
|
|
- **gulp** 3.9.1 - Task runner
|
|
- **nodemon** 1.11.0 - Auto-restart on file changes
|
|
- **mocha** 3.0.1 - Test framework
|
|
- **chai** 3.5.0 - Assertion library
|
|
|
|
---
|
|
|
|
## 📁 Project Structure
|
|
|
|
```
|
|
backend/
|
|
├── src/
|
|
│ ├── app.js # Express app configuration
|
|
│ │
|
|
│ ├── bin/
|
|
│ │ └── www # Server startup script (port 3069)
|
|
│ │
|
|
│ ├── routes/ # API route handlers
|
|
│ │ ├── auth.js # Authentication (login, logout, password reset)
|
|
│ │ ├── profiles.js # Profile CRUD + approval workflow
|
|
│ │ ├── users.js # User management
|
|
│ │ └── geocache.js # Geolocation/geocoding
|
|
│ │
|
|
│ ├── models/ # Mongoose schemas
|
|
│ │ ├── user.js # User schema (auth, permissions)
|
|
│ │ ├── profile.js # Profile schema (details, messages)
|
|
│ │ ├── message.js # Message schema (text, image, timestamp)
|
|
│ │ ├── reset.js # Password reset token schema
|
|
│ │ └── geocache.js # Location cache schema
|
|
│ │
|
|
│ ├── modules/ # Utility modules
|
|
│ │ ├── authentication.js # PBKDF2 password hashing
|
|
│ │ ├── token.js # JWT creation/verification
|
|
│ │ ├── images.js # Image upload/processing (multer)
|
|
│ │ ├── mailer.js # Email sending (nodemailer)
|
|
│ │ ├── geocoder.js # Google Maps geocoding
|
|
│ │ └── logger.js # Winston logging configuration
|
|
│ │
|
|
│ └── images/ # Uploaded image storage
|
|
│ ├── profile/ # Profile photos (thumb + detail)
|
|
│ ├── message/ # Message attachments
|
|
│ └── cruise/ # Additional images
|
|
│
|
|
├── data/
|
|
│ └── profiles.json # Seed data (source of truth)
|
|
│
|
|
├── package.json # Dependencies and scripts
|
|
├── gulpfile.js # Gulp tasks (dev, test)
|
|
└── Dockerfile # Docker build configuration
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- Node.js 14.x (included in DevContainer)
|
|
- MongoDB 4.4 (included in DevContainer)
|
|
- Environment variables configured (see [Environment Variables](#environment-variables))
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
# From project root
|
|
npm run install:all
|
|
|
|
# Or from backend directory
|
|
cd backend
|
|
npm install
|
|
```
|
|
|
|
### Start Development Server
|
|
|
|
```bash
|
|
# From project root (recommended)
|
|
npm run dev:backend
|
|
|
|
# Or from backend directory
|
|
cd backend
|
|
npm run dev
|
|
|
|
# Or using gulp directly
|
|
gulp
|
|
```
|
|
|
|
Server starts on **port 3069** with auto-restart via nodemon.
|
|
|
|
### Verify Server is Running
|
|
|
|
```bash
|
|
curl http://localhost:3069/profiles
|
|
```
|
|
|
|
Expected: JSON array of profiles or 404 if no data seeded.
|
|
|
|
---
|
|
|
|
## 🔄 Data Flow
|
|
|
|
### Request Lifecycle
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[Client HTTP Request] --> B[Express App app.js]
|
|
B --> C[Morgan Logger logs request]
|
|
C --> D[Body Parser parses JSON]
|
|
D --> E[CORS Headers added]
|
|
E --> F{Route Match?}
|
|
F -->|Yes| G[Route Handler routes/*]
|
|
F -->|No| H[404 Error Handler]
|
|
G --> I{Auth Required?}
|
|
I -->|Yes| J[Token.verifyThen]
|
|
I -->|No| K[Execute Controller Logic]
|
|
J --> L{Valid Token?}
|
|
L -->|Yes| M{Has Permission?}
|
|
L -->|No| N[403 Forbidden]
|
|
M -->|Yes| K
|
|
M -->|No| N
|
|
K --> O[Mongoose Model models/*]
|
|
O --> P[MongoDB Query]
|
|
P --> Q[Pre-Save Hooks?]
|
|
Q -->|Image Processing| R[modules/images]
|
|
R --> S[Save to Filesystem]
|
|
S --> T[Update Document Path]
|
|
Q -->|No Hooks| U[Save to DB]
|
|
T --> U
|
|
U --> V[Return Response]
|
|
V --> W[Winston Logger logs result]
|
|
W --> X[Send JSON to Client]
|
|
```
|
|
|
|
### Example: Message Creation with Image
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[POST /profiles/:id/message] --> B[Token Verification]
|
|
B --> C[Extract message data from request body]
|
|
C --> D[Message.save pre-hook triggered]
|
|
D --> E{message.image is object?}
|
|
E -->|Yes Base64 data| F[Images.saveMessageImage]
|
|
E -->|No string path| G[Skip processing]
|
|
F --> H[Decode Base64]
|
|
H --> I[Generate unique filename]
|
|
I --> J[Write to src/images/message/]
|
|
J --> K[Return filename path]
|
|
K --> L[Update message.image = filename]
|
|
L --> M[Save to MongoDB]
|
|
G --> M
|
|
M --> N[Emit event to route handler]
|
|
N --> O[Return 200 with message data]
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 Environment Variables
|
|
|
|
**Required environment variables** must be configured in [`.env.example`](.env.example).
|
|
|
|
### Create Environment File
|
|
|
|
```bash
|
|
# Copy example file
|
|
cp .env.example .env
|
|
|
|
# Edit with your values
|
|
nano .env
|
|
```
|
|
|
|
### Required Variables
|
|
|
|
| Variable | Description | Example |
|
|
| ----------------------- | --------------------------------- | ------------------------------------ |
|
|
| **PORT** | Server port | `3069` |
|
|
| **MONGODB_URI** | MongoDB connection string | `mongodb://mongo:27017/urge` |
|
|
| **JWT_SECRET** | Secret key for JWT signing | `your-super-secret-key-min-32-chars` |
|
|
| **GOOGLE_MAPS_API_KEY** | Google Maps API key for geocoding | `AIzaSy...` |
|
|
| **MAIL_HOST** | SMTP server hostname | `smtp.gmail.com` |
|
|
| **MAIL_PORT** | SMTP server port | `587` |
|
|
| **MAIL_USER** | SMTP username | `support@example.com` |
|
|
| **MAIL_PASS** | SMTP password | `your-password` |
|
|
|
|
### Security Notes
|
|
|
|
- **JWT_SECRET**: Use a random 32+ character string. Generate with:
|
|
```bash
|
|
openssl rand -base64 32
|
|
```
|
|
- **MAIL_PASS**: Use app-specific passwords for Gmail/G Suite
|
|
- **GOOGLE_MAPS_API_KEY**: Restrict API key to your backend IP/domain
|
|
- **Never commit** `.env` files to version control
|
|
|
|
📄 **See [.env.example](.env.example) for complete configuration with descriptions**
|
|
|
|
---
|
|
|
|
## 💾 Database Seeding
|
|
|
|
### ⚠️ Important: Data Entry Workflow
|
|
|
|
**This application does NOT support interactive data entry through the UI.** All profile and message data must be added to the seed file and database reseeded.
|
|
|
|
### Seed Data Location
|
|
|
|
**Source of Truth**: [`data/profiles.json`](data/profiles.json)
|
|
|
|
### How to Add/Edit Profiles
|
|
|
|
1. **Edit `data/profiles.json`**:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"order": 1,
|
|
"details": {
|
|
"name": "John",
|
|
"age": 28,
|
|
"location": "San Francisco, CA",
|
|
"about": "User's story...",
|
|
"pic": {
|
|
"thumb": "profile/john_thumbnail.png",
|
|
"detail": "profile/john_detail.png"
|
|
},
|
|
"position": ["Top", "Versatile"],
|
|
"looking": ["Dates", "Friends"],
|
|
"tribes": ["Geek", "Jock"],
|
|
"ethnos": ["White", "Latino"]
|
|
},
|
|
"messages": [
|
|
{
|
|
"text": "What brought you to dating apps?",
|
|
"isUser": false,
|
|
"timestamp": "2024-01-15T10:30:00Z"
|
|
},
|
|
{
|
|
"text": "I moved to a new city and wanted to meet people...",
|
|
"isUser": true,
|
|
"timestamp": "2024-01-15T10:32:00Z"
|
|
}
|
|
],
|
|
"submitted": true,
|
|
"approved": true
|
|
}
|
|
]
|
|
```
|
|
|
|
2. **Add images** to `src/images/profile/` and `src/images/message/`
|
|
|
|
3. **Run seed script**:
|
|
|
|
```bash
|
|
# From project root
|
|
npm run seed
|
|
|
|
# Or from backend directory
|
|
cd backend
|
|
npm run seed
|
|
```
|
|
|
|
4. **Verify data loaded**:
|
|
```bash
|
|
curl http://localhost:3069/profiles
|
|
```
|
|
|
|
### Seed Script Behavior
|
|
|
|
- **Drops existing database** (all data wiped)
|
|
- **Creates fresh collections** from schema
|
|
- **Loads data** from `data/profiles.json`
|
|
- **Processes images** via pre-save hooks
|
|
- **No backup created** - commit changes to Git first
|
|
|
|
### Production Deployment
|
|
|
|
⚠️ **Database is wiped and reseeded on each deployment**. All content changes must be committed to `data/profiles.json` before deploying.
|
|
|
|
If the application becomes interactive with user-generated content, implement proper backup strategies (see [DEPLOYMENT.md](../DEPLOYMENT.md)).
|
|
|
|
---
|
|
|
|
## 💻 Development Workflow
|
|
|
|
### Gulp Tasks
|
|
|
|
The project uses **Gulp** for development automation.
|
|
|
|
**Default task** (auto-restart on changes):
|
|
|
|
```bash
|
|
gulp
|
|
```
|
|
|
|
This runs:
|
|
|
|
- **nodemon** - Restarts server on `.js` file changes
|
|
- **mocha** - Runs tests on changes
|
|
- Watches `src/**/*.js` for changes
|
|
|
|
**Test task**:
|
|
|
|
```bash
|
|
gulp test
|
|
```
|
|
|
|
Runs Mocha tests with "nyan" reporter (cat animation 🐱).
|
|
|
|
### Manual Development
|
|
|
|
Without Gulp:
|
|
|
|
```bash
|
|
# Start with nodemon
|
|
nodemon src/bin/www
|
|
|
|
# Or plain Node.js
|
|
node src/bin/www
|
|
```
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
# Run tests
|
|
npm test
|
|
|
|
# Or with Gulp
|
|
gulp test
|
|
```
|
|
|
|
**Note**: Test coverage is limited. Most tests are in backend, none in frontend.
|
|
|
|
### Logging
|
|
|
|
**Winston logger** outputs to:
|
|
|
|
- **Console**: Colorized logs during development
|
|
- **Files**: `logs/error.log`, `logs/combined.log` (if configured)
|
|
|
|
**Morgan HTTP logger** logs all requests:
|
|
|
|
```
|
|
GET /profiles 200 45ms - 2.5kb
|
|
POST /auth/login 401 12ms - 87b
|
|
```
|
|
|
|
### Code Linting
|
|
|
|
```bash
|
|
# From project root
|
|
npm run lint:backend
|
|
|
|
# Or from backend directory
|
|
cd backend
|
|
npm run lint
|
|
```
|
|
|
|
**Note**: No ESLint config in backend currently. Consider adding:
|
|
|
|
```bash
|
|
npm install -D eslint
|
|
npx eslint --init
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Troubleshooting
|
|
|
|
### MongoDB Connection Failed
|
|
|
|
**Error:**
|
|
|
|
```
|
|
MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Verify MongoDB is running**:
|
|
|
|
```bash
|
|
docker ps | grep mongo
|
|
```
|
|
|
|
2. **Check connection string**:
|
|
|
|
```bash
|
|
echo $MONGODB_URI
|
|
# Should be: mongodb://mongo:27017/urge (DevContainer)
|
|
# Or: mongodb://localhost:27017/urge (local)
|
|
```
|
|
|
|
3. **Test connection**:
|
|
|
|
```bash
|
|
docker exec -it <mongo-container> mongo --eval "db.adminCommand('ping')"
|
|
```
|
|
|
|
4. **Restart MongoDB**:
|
|
```bash
|
|
docker-compose -f .devcontainer/docker-compose.yml restart mongo
|
|
```
|
|
|
|
---
|
|
|
|
### JWT Token Invalid
|
|
|
|
**Error:**
|
|
|
|
```
|
|
JsonWebTokenError: invalid signature
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Verify JWT_SECRET is set**:
|
|
|
|
```bash
|
|
echo $JWT_SECRET
|
|
```
|
|
|
|
2. **Check .env file exists**:
|
|
|
|
```bash
|
|
ls -la .env
|
|
```
|
|
|
|
3. **Restart backend** after changing environment variables:
|
|
|
|
```bash
|
|
npm run dev:backend
|
|
```
|
|
|
|
4. **Generate new token** by logging in again
|
|
|
|
---
|
|
|
|
### Image Upload Fails
|
|
|
|
**Error:**
|
|
|
|
```
|
|
ENOENT: no such file or directory, open 'src/images/profile/...'
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Create image directories**:
|
|
|
|
```bash
|
|
mkdir -p src/images/profile src/images/message src/images/cruise
|
|
```
|
|
|
|
2. **Check permissions**:
|
|
|
|
```bash
|
|
chmod -R 755 src/images
|
|
```
|
|
|
|
3. **Verify volume mount** in DevContainer:
|
|
|
|
```bash
|
|
ls -la src/images
|
|
```
|
|
|
|
4. **Check multer configuration** in `modules/images.js`
|
|
|
|
---
|
|
|
|
### Port 3069 Already in Use
|
|
|
|
**Error:**
|
|
|
|
```
|
|
Error: listen EADDRINUSE: address already in use :::3069
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Find process using port 3069
|
|
lsof -i :3069
|
|
|
|
# Kill the process
|
|
kill -9 <PID>
|
|
|
|
# Or use different port
|
|
export PORT=3070
|
|
npm run dev
|
|
```
|
|
|
|
---
|
|
|
|
### Seed Script Fails
|
|
|
|
**Error:**
|
|
|
|
```
|
|
Error: Cannot find module 'data/profiles.json'
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Verify file exists**:
|
|
|
|
```bash
|
|
ls -la data/profiles.json
|
|
```
|
|
|
|
2. **Check JSON syntax**:
|
|
|
|
```bash
|
|
node -e "JSON.parse(require('fs').readFileSync('data/profiles.json'))"
|
|
```
|
|
|
|
3. **Run from correct directory**:
|
|
```bash
|
|
cd backend
|
|
npm run seed
|
|
```
|
|
|
|
---
|
|
|
|
### Google Maps API Errors
|
|
|
|
**Error:**
|
|
|
|
```
|
|
REQUEST_DENIED: The provided API key is invalid
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Set API key in environment**:
|
|
|
|
```bash
|
|
export GOOGLE_MAPS_API_KEY="your-key-here"
|
|
```
|
|
|
|
2. **Enable APIs** in Google Cloud Console:
|
|
|
|
- Geocoding API
|
|
- Places API (if used)
|
|
|
|
3. **Check API restrictions** (IP/domain whitelisting)
|
|
|
|
4. **Verify billing enabled** (Google requires it even for free tier)
|
|
|
|
---
|
|
|
|
### Email Sending Fails
|
|
|
|
**Error:**
|
|
|
|
```
|
|
Invalid login: 535-5.7.8 Username and Password not accepted
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Use app-specific password** (Gmail):
|
|
|
|
- Go to Google Account → Security → 2-Step Verification → App passwords
|
|
- Generate password for "Mail"
|
|
- Use that password in MAIL_PASS
|
|
|
|
2. **Verify SMTP settings**:
|
|
|
|
```bash
|
|
echo $MAIL_HOST $MAIL_PORT $MAIL_USER
|
|
```
|
|
|
|
3. **Test SMTP connection**:
|
|
|
|
```bash
|
|
telnet $MAIL_HOST $MAIL_PORT
|
|
```
|
|
|
|
4. **Enable "Less secure app access"** (not recommended, use app passwords instead)
|
|
|
|
---
|
|
|
|
## 📚 Related Documentation
|
|
|
|
- **[API Reference](API.md)** - Complete REST API endpoint documentation
|
|
- **[Database Schema](SCHEMA.md)** - MongoDB collection schemas and relationships
|
|
- **[Environment Variables](.env.example)** - Configuration template
|
|
- **[Root README](../README.md)** - Project overview and quick start
|
|
- **[Frontend README](../app/README.md)** - Ionic app documentation
|
|
- **[DevContainer Guide](../.devcontainer/README.md)** - Development environment
|
|
- **[Deployment Guide](../DEPLOYMENT.md)** - Production deployment
|
|
|
|
---
|
|
|
|
**Need Help?** Check the [root troubleshooting section](../README.md#troubleshooting) or API documentation for endpoint-specific issues.
|