Backend Documentation

Overview

The backend system is built on Node.js and Express, providing a RESTful API for the music collaboration platform. It handles user authentication, project management, file uploads, version history, feature requests, and comments.

Core Technologies

  • Node.js - JavaScript runtime environment
  • Express.js - Web application framework
  • Passport.js - Authentication middleware
  • Supabase - Database provider
  • Git - Version control for project files

Setup & Installation

Prerequisites

  • Node.js (v14 or higher)
  • npm or yarn
  • Git
  • Supabase account
  • GitHub OAuth application

Environment Variables

Create a .env file in the backend directory with the following variables:

SESSION_SECRET=your_session_secret
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
SUPABASE_URL=your_supabase_url
SUPABASE_KEY=your_supabase_key
REPOSITORY_PATH=path_to_repositories

Installation

# Clone the repository
git clone https://github.com/yourusername/cs506.git
cd cs506/backend

# Install dependencies
npm install

# Start the server
npm start

Architecture

The backend follows a modular architecture with clear separation of concerns:

Client Application

React Frontend

Express Server

Routes, Middleware, Controllers

Services

Git, Supabase

Utils

Audio Processing, Helpers

Database

Supabase (PostgreSQL)

File Storage

Git Repositories

Directory Structure

backend/
├── config/              # Configuration files
├── middlewares/         # Express middlewares
├── repositories/        # Repository pattern implementations
├── routes/              # API route definitions
├── services/            # Business logic services
├── tmp/                 # Temporary files and repositories
├── utils/               # Utility functions
├── .env                 # Environment variables
├── app.js               # Express app setup
└── index.js             # Entry point

Authentication

The application uses Passport.js with GitHub OAuth for authentication. Session data is stored using express-session.

Authentication Flow

  1. User initiates login by clicking "Login with GitHub" button
  2. User is redirected to GitHub for authorization
  3. After successful authorization, GitHub redirects back to our callback URL
  4. A user record is created or retrieved in the database
  5. The user is redirected to the dashboard with an authenticated session

Implementation

The authentication is implemented in:

  • utils/passport.js - Configures the Passport GitHub strategy
  • routes/authentication.js - Defines authentication routes
  • utils/createOrGetUser.js - Creates or retrieves user records

Session Management

Sessions are configured in app.js using express-session middleware with the following options:

  • HTTP-only cookies for security
  • Non-secure cookies for development (should be secure in production)
  • SameSite: "lax" for CSRF protection

API Endpoints

Authentication Endpoints

GET /auth/github

Initiates GitHub OAuth flow

GET /auth/github/callback

GitHub OAuth callback handler

GET /auth/logout

Logs out the user and destroys the session

GET /api/me

Returns the current authenticated user's information

Projects Endpoints

POST /api/projects

Creates a new project

Request Body
{
  "userId": "string", // GitHub user ID
  "title": "string",
  "hashtags": "string" | string[], // Optional - comma-separated or array
  "description": "string" // Optional
}
Response
{
  "id": "string",
  "title": "string",
  "user_id": "string",
  "hashtags": string[],
  "created_at": "timestamp",
  "updated_at": "timestamp"
}
GET /api/projects?owner_id=:userId

Gets all projects for a user

Response
[
  {
    "id": "string",
    "title": "string",
    "user_id": "string",
    "hashtags": string[],
    "created_at": "timestamp",
    "updated_at": "timestamp",
    "User": {
      "name": "string"
    }
  },
  // ...
]
GET /api/projects/:projectId

Gets a project by ID

Response
{
  "id": "string",
  "title": "string",
  "user_id": "string",
  "hashtags": string[],
  "created_at": "timestamp",
  "updated_at": "timestamp",
  "User": {
    "name": "string"
  }
}

Upload Endpoints

POST /api/upload/:projectId

Uploads files to a project and commits them to the repository

Request Body (multipart/form-data)
files: File[] // The files to upload
metadata.json: File // JSON file containing metadata (userId, projectId, commitMessage)
Response
{
  "success": true,
  "message": "Files uploaded and committed successfully",
  "commitId": "string",
  "diffSummary": {
    "totalChanges": number,
    "changedTracks": string[],
    "addedTracks": string[],
    "removedTracks": string[],
    "modifiedTracks": string[]
  }
}

History Endpoints

GET /api/history/all/:userId/:projectId

Gets the commit history for a project including detailed change information

Response
{
  "projectId": "string",
  "userId": "string",
  "history": {
    "total": number,
    "all": [
      {
        "hash": "string",
        "date": "timestamp",
        "message": "string",
        "author": "string",
        "trackChanges": {
          "summary": {
            "totalChanges": number,
            "changedTracks": string[],
            "addedTracks": string[],
            "removedTracks": string[]
          },
          "noteChanges": object[],
          "velocityChanges": object[],
          "trackParameterChanges": object[]
        }
      },
      // ...
    ]
  }
}
GET /api/history/latest/:userId/:projectId

Gets the latest commit content as a ZIP file

Response

Binary ZIP file containing the project files

GET /api/history/diff/:userId/:projectId/:commitHash

Gets the detailed diff information for a specific commit

Response
{
  "summary": {
    "totalChanges": number,
    "changedTracks": string[],
    "addedTracks": string[],
    "removedTracks": string[],
    "modifiedTracks": string[]
  },
  "noteChanges": [
    {
      "type": "noteAdded" | "noteRemoved" | "noteModified",
      "trackName": "string",
      "note": "string",
      "beat": number,
      "description": "string"
    }
  ],
  "velocityChanges": [
    {
      "type": "velocity",
      "trackName": "string",
      "note": "string",
      "beat": number,
      "from": number,
      "to": number,
      "description": "string"
    }
  ],
  "trackParameterChanges": [
    {
      "type": "volume" | "pan" | "other",
      "trackName": "string", 
      "parameter": "string",
      "from": number,
      "to": number,
      "description": "string" 
    }
  ],
  "loopChanges": [],
  "audioFileChanges": [],
  "trackAddRemove": []
}
GET /api/history/:userId/:projectId/:commitHash

Gets a specific commit's content as a ZIP file

Response

Binary ZIP file containing the project files at the specified commit

POST /api/history/restore/:userId/:projectId/:commitHash

Restores a project to the state of a specific commit

Request Body
{
  "message": "string" // Optional commit message
}
Response
{
  "message": "Version restored successfully",
  "projectId": "string",
  "userId": "string"
}

Features Endpoints

GET /api/features/:projectId

Gets all feature requests for a project

Response
[
  {
    "id": number,
    "project_id": "string",
    "author_id": "string",
    "message": "string",
    "description": "string",
    "label": "string",
    "priority": "string", // "high", "medium", "low"
    "open": boolean,
    "votes": number,
    "created_at": "timestamp",
    "updated_at": "timestamp",
    "User": {
      "name": "string"
    }
  },
  // ...
]
POST /api/features

Creates a new feature request

Request Body
{
  "project_id": "string",
  "author_id": "string",
  "message": "string",
  "description": "string",
  "label": "string",
  "open": boolean,
  "priority": "string" // "high", "medium", "low"
}
Response
{
  "id": number,
  "project_id": "string",
  "author_id": "string",
  "message": "string",
  "description": "string",
  "label": "string",
  "priority": "string",
  "open": boolean,
  "votes": 0,
  "created_at": "timestamp",
  "updated_at": "timestamp"
}
PUT /api/features/:featureId

Updates a feature request (e.g., close it)

Request Body
{
  "open": boolean
}
Response
{
  "success": true,
  "message": "Feature updated successfully"
}

Comments Endpoints

GET /api/comments/:featureId

Gets all comments for a feature request

POST /api/comments

Creates a new comment on a feature request

Request Body
{
  "feature_id": number,
  "author_id": "string",
  "content": "string"
}

Admin Endpoints

GET /api/admin/users

Gets all users with their projects (admin access only)

Response
[
  {
    "id": "string",
    "name": "string",
    "created_at": "timestamp",
    "photo": "string",
    "projects": [
      {
        "id": "string",
        "title": "string",
        "created_at": "timestamp",
        "updated_at": "timestamp"
      },
      // ...
    ]
  },
  // ...
]

AI Endpoints

POST /api/ai/generate-commit-message

Generates an AI-powered commit message based on project changes

Request Body
{
  "diffSummary": {
    "llmSummary": "string", // Summary of changes in natural language
    // other diff properties
  },
  "options": {
    "style": "string", // Optional style parameter
    // other options
  }
}
Response
{
  "success": true,
  "commitMessage": "string",
  "explanation": "string" // Optional explanation of changes
}
GET /api/ai/commit-style-options

Gets available commit message style options

Response
{
  "styles": ["concise", "detailed", "technical", ...],
  "defaultStyle": "string"
}

Collaboration Endpoints

POST /api/collabs/:collab_id

Updates the status of a collaboration request (accept/reject)

Request Body
{
  "action": "string" // "accepted", "rejected", or other valid status
}
Response
{
  "message": "Collaboration request status: {status}",
  "files": ["string"] // List of files (if applicable)
}

Profile Endpoints

GET /api/profile/:userId

Gets a user's profile and comprehensive statistics

Response
{
  "user": {
    "id": "string",
    "name": "string",
    "created_at": "timestamp",
    "photo": "string"
  },
  "stats": {
    "projects": {
      "total": number,
      "public": number,
      "private": number,
      "projects": array,
      "recentActivity": array,
      "mostUsedTags": array
    },
    "features": {
      "created": number,
      "open": number,
      "closed": number,
      "byPriority": {
        "high": number,
        "medium": number,
        "low": number
      },
      "features": array
    },
    "collabs": {
      "total": number,
      "pending": number,
      "accepted": number,
      "rejected": number,
      "collabs": array
    },
    "comments": {
      "total": number,
      "byFeature": array,
      "comments": array
    },
    "activityData": array,
    "engagementScore": number
  }
}

Audio Endpoints

POST /api/audio/compare

Compares two WAV files for similarity

Request Body
{
  // Request format needs to be implemented
  // Currently a work in progress in the codebase
}
Response
{
  "similarity": number // Similarity score between 0 and 1
}

Services

Services encapsulate the core business logic of the application.

Git Service

Manages Git repositories for projects, including:

  • createAbletonRepo(userId, projectId) - Creates a new repository for a project
  • commitFiles(userId, projectId, files, message) - Commits files to a repository
  • getHistory(userId, projectId) - Gets the commit history
  • getContent(userId, projectId, commitId) - Gets content at a specific commit
  • getDiffJSON(commitHash) - Gets the diff.json file containing detailed changes
  • createArchive(commitHash) - Creates a ZIP archive of the repository at a specific commit
  • restoreCommit(commitHash, userId, message) - Restores repository to a previous commit state
  • Supabase Service

    Provides a connection to the Supabase database:

    // services/supabase.js
    const { createClient } = require('@supabase/supabase-js');
    require('dotenv').config();
    const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE);
    
    module.exports = supabase;

    Utilities

    The utils directory contains helper functions and utilities:

    Audio Processing

    • audioComparison.js - Compares audio files for similarity
    • compresswav.py - Python script for WAV file compression
    • parseAbleton.py - Python script to extract data from Ableton Live project files
    • trackComparison.js - Compares music tracks to detect changes between versions

    Upload Helpers

    uploadHelpers.js provides utilities for handling file uploads:

    • Configures Busboy for multipart form parsing
    • Processes uploaded files and metadata
    • Handles file storage and cleanup

    User Management

    • createOrGetUser.js - Creates a new user or retrieves an existing one
    • passport.js - Configures Passport.js for authentication

    Repository

    The repo subdirectory contains utilities for working with Git repositories.

    Configuration

    The config directory contains configuration files:

    init.js

    Initializes the application, sets up directories, and defines constants:

    // config/init.js
    const fs = require('fs');
    const path = require('path');
    
    // Repository path where project files are stored
    const REPOSITORY_PATH = process.env.REPOSITORY_PATH || path.join(__dirname, '../tmp/repositories');
    // Path for temporary uploads
    const UPLOAD_PATH = process.env.UPLOAD_PATH || path.join(__dirname, '../tmp/uploads');
    // Path for ZIP archives
    const ARCHIVE_PATH = process.env.ARCHIVE_PATH || path.join(__dirname, '../tmp/archives');
    
    // Initialize function creates necessary directories
    function init() {
      // Create directories if they don't exist
      [REPOSITORY_PATH, UPLOAD_PATH, ARCHIVE_PATH].forEach(dir => {
        if (!fs.existsSync(dir)) {
          fs.mkdirSync(dir, { recursive: true });
        }
      });
      
      // Additional initialization tasks...
    }
    
    module.exports = { init, REPOSITORY_PATH, UPLOAD_PATH, ARCHIVE_PATH };

    Diff Engine

    The diff engine is a core component that compares Ableton project versions and generates detailed change reports.

    Overview

    The diff engine analyzes two versions of an Ableton project and identifies changes including:

    • Added, removed, or modified notes
    • Volume changes for tracks
    • Parameter changes (pan, effects, etc.)
    • Added or removed tracks
    • Audio file changes

    Implementation

    The DiffEngine class in trackComparison.js contains the core logic:

    class DiffEngine {
        constructor() {
            this.comparators = [];       // Project-level comparators
            this.trackLevelComparators = []; // Track-level comparators
        }
    
        // Register track-level comparators for comparing specific aspects of tracks
        registerTrackComparator(comparator) { ... }
    
        // Register project-level comparators for comparing overall project changes
        registerProjectComparator(comparator) { ... }
    
        // Create a detailed diff between two project versions
        createDetailedDiff(oldVersion, newVersion) { ... }
    }

    Comparators

    The engine uses a plugin system of comparators, each responsible for detecting a specific type of change:

    • NoteComparator - Detects added, removed, or modified MIDI notes
    • VelocityComparator - Detects changes in note velocities
    • TrackParameterComparator - Detects changes in track parameters like volume
    • LoopComparator - Detects changes in audio loop settings
    • AudioFileComparator - Detects changes in audio files

    Sample Diff Output

    {
      "summary": {
        "totalChanges": 8,
        "changedTracks": ["2-Clap 909", "3-Drum Rack", "6-Chord Dub Pluck"],
        "addedTracks": [],
        "removedTracks": [],
        "modifiedTracks": ["2-Clap 909", "3-Drum Rack", "6-Chord Dub Pluck"]
      },
      "noteChanges": [
        {
          "type": "noteRemoved",
          "trackName": "6-Chord Dub Pluck",
          "note": "55",
          "beat": 52,
          "description": "Note 55 at beat 52.00 was removed from track '6-Chord Dub Pluck'"
        },
        {
          "type": "noteAdded",
          "trackName": "6-Chord Dub Pluck",
          "note": "43",
          "beat": 52,
          "description": "Note 43 at beat 52.00 was added to track '6-Chord Dub Pluck'"
        }
      ],
      "trackParameterChanges": [
        {
          "type": "volume",
          "trackName": "2-Clap 909",
          "parameter": "volume",
          "from": 0.6025595665,
          "to": 0.5308843851,
          "description": "Volume for '2-Clap 909' changed from 0.603 to 0.531"
        }
      ]
    }

    Integration with Git

    The diff engine is integrated with Git operations:

    • When files are committed, changes are analyzed and stored as diff.json
    • Changes are also embedded in commit messages for quick reference
    • The diff data can be retrieved via the /api/history/diff/:userId/:projectId/:commitHash endpoint

    Database Schema

    The application uses Supabase (PostgreSQL) as its database. Here's the schema:

    User Table

    Column Type Description
    id string GitHub user ID (primary key)
    name string User's display name
    created_at timestamp When the user was created
    photo string URL to user's profile picture (optional)

    Project Table

    Column Type Description
    id string Unique project ID (primary key)
    title string Project title
    user_id string Owner's user ID (foreign key to User.id)
    hashtags string[] Array of hashtags
    created_at timestamp When the project was created
    updated_at timestamp When the project was last updated

    Feature Table

    Column Type Description
    id number Feature ID (primary key)
    project_id string Project ID (foreign key to Project.id)
    author_id string Author's user ID (foreign key to User.id)
    message string Feature title/message
    description string Feature description
    label string Feature category/label
    priority string Priority level (high, medium, low)
    open boolean Whether the feature request is open
    votes number Number of votes
    created_at timestamp When the feature was created
    updated_at timestamp When the feature was last updated

    Comment Table

    Column Type Description
    id number Comment ID (primary key)
    feature_id number Feature ID (foreign key to Feature.id)
    author_id string Author's user ID (foreign key to User.id)
    content string Comment content
    created_at timestamp When the comment was created