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
- User initiates login by clicking "Login with GitHub" button
- User is redirected to GitHub for authorization
- After successful authorization, GitHub redirects back to our callback URL
- A user record is created or retrieved in the database
- The user is redirected to the dashboard with an authenticated session
Implementation
The authentication is implemented in:
utils/passport.js- Configures the Passport GitHub strategyroutes/authentication.js- Defines authentication routesutils/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
Initiates GitHub OAuth flow
GitHub OAuth callback handler
Logs out the user and destroys the session
Returns the current authenticated user's information
Projects Endpoints
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"
}
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"
}
},
// ...
]
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
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
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[]
}
},
// ...
]
}
}
Gets the latest commit content as a ZIP file
Response
Binary ZIP file containing the project files
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": []
}
Gets a specific commit's content as a ZIP file
Response
Binary ZIP file containing the project files at the specified commit
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
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"
}
},
// ...
]
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"
}
Updates a feature request (e.g., close it)
Request Body
{
"open": boolean
}
Response
{
"success": true,
"message": "Feature updated successfully"
}
Admin Endpoints
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
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
}
Gets available commit message style options
Response
{
"styles": ["concise", "detailed", "technical", ...],
"defaultStyle": "string"
}
Collaboration Endpoints
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
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
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 projectcommitFiles(userId, projectId, files, message) - Commits files to a repositorygetHistory(userId, projectId) - Gets the commit historygetContent(userId, projectId, commitId) - Gets content at a specific commitgetDiffJSON(commitHash) - Gets the diff.json file containing detailed changescreateArchive(commitHash) - Creates a ZIP archive of the repository at a specific commitrestoreCommit(commitHash, userId, message) - Restores repository to a previous commit stateSupabase 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 similaritycompresswav.py- Python script for WAV file compressionparseAbleton.py- Python script to extract data from Ableton Live project filestrackComparison.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 onepassport.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/:commitHashendpoint
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 |
Comments Endpoints
Gets all comments for a feature request
Creates a new comment on a feature request
Request Body