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

16 KiB

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

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)

Installation

# From project root
npm run install:all

# Or from backend directory
cd backend
npm install

Start Development Server

# 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

curl http://localhost:3069/profiles

Expected: JSON array of profiles or 404 if no data seeded.


🔄 Data Flow

Request Lifecycle

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

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.

Create Environment File

# 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:
    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 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

How to Add/Edit Profiles

  1. Edit data/profiles.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:

    # From project root
    npm run seed
    
    # Or from backend directory
    cd backend
    npm run seed
    
  4. Verify data loaded:

    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).


💻 Development Workflow

Gulp Tasks

The project uses Gulp for development automation.

Default task (auto-restart on changes):

gulp

This runs:

  • nodemon - Restarts server on .js file changes
  • mocha - Runs tests on changes
  • Watches src/**/*.js for changes

Test task:

gulp test

Runs Mocha tests with "nyan" reporter (cat animation 🐱).

Manual Development

Without Gulp:

# Start with nodemon
nodemon src/bin/www

# Or plain Node.js
node src/bin/www

Testing

# 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

# 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:

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:

    docker ps | grep mongo
    
  2. Check connection string:

    echo $MONGODB_URI
    # Should be: mongodb://mongo:27017/urge (DevContainer)
    # Or: mongodb://localhost:27017/urge (local)
    
  3. Test connection:

    docker exec -it <mongo-container> mongo --eval "db.adminCommand('ping')"
    
  4. Restart MongoDB:

    docker-compose -f .devcontainer/docker-compose.yml restart mongo
    

JWT Token Invalid

Error:

JsonWebTokenError: invalid signature

Solutions:

  1. Verify JWT_SECRET is set:

    echo $JWT_SECRET
    
  2. Check .env file exists:

    ls -la .env
    
  3. Restart backend after changing environment variables:

    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:

    mkdir -p src/images/profile src/images/message src/images/cruise
    
  2. Check permissions:

    chmod -R 755 src/images
    
  3. Verify volume mount in DevContainer:

    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:

# 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:

    ls -la data/profiles.json
    
  2. Check JSON syntax:

    node -e "JSON.parse(require('fs').readFileSync('data/profiles.json'))"
    
  3. Run from correct directory:

    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:

    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:

    echo $MAIL_HOST $MAIL_PORT $MAIL_USER
    
  3. Test SMTP connection:

    telnet $MAIL_HOST $MAIL_PORT
    
  4. Enable "Less secure app access" (not recommended, use app passwords instead)



Need Help? Check the root troubleshooting section or API documentation for endpoint-specific issues.