Documentation
This commit is contained in:
652
backend/README.md
Normal file
652
backend/README.md
Normal file
@@ -0,0 +1,652 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user