Files
looking-monorepo/backend/README.md
2025-12-28 13:52:25 -03:00

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.