Skip to content

About

A .NET 9 Web API that powers collaborative Spotify playlists. Handles Spotify OAuth, room management, and track proposals, with JWT authentication for owners. Uses PostgreSQL (via Supabase) for persistence. Provides secure endpoints for creating rooms, proposing tracks, and syncing approved songs into Spotify playlists.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

455 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

.NET version Project licence Repo size

TuneMates - Backend

TuneMates is a web application that allows users to create and join music listening rooms, where they can share and enjoy music together in real-time. The backend is built using .NET 9 in C#.

This repository contains the backend code for the TuneMates application, which handles user authentication, room management, song proposals, and integration with the Spotify API. You can find the frontend part here.

The frontend is hosted on Vercel. For the moment the backend still need to be self-hosted (localhost).

Features

  • User authentication and authorization
  • Create and join rooms to add songs to a shared Spotify playlist
  • JWT token generation for secure communication
  • Integration with Spotify API for song search and playlist management
  • Background services for cleaning up expired tokens and proposals
  • Database management using Entity Framework Core with Supabase as the backend service
  • Password-protected rooms for added security

Libraries and Tools

Running the Application

Running with .NET

  1. Clone the repository:
    git clone https://github.com/NamelessProj/TuneMates_Backend.git 
    dotnet run
  2. Use a tool like Postman to interact with the API endpoints at https://localhost:7016.

Don't worry about the dependencies, they will be restored automatically when you run the project. So when you'll run the project for the first time, it may take a bit longer becvause it has to download everything. But after that, next runs will be faster.

If you don't have .NET 9 installed, you can download it from the .NET official website. It's necessary to run the project locally without Docker.

Running with Docker

The project includes a Dockerfile for containerized deployment with .NET 9.0, ensuring consistent runtime environments across different hosts.

Everything you need to know

Configuration Options

Docker supports three ways to configure the application:

  1. Mount appsettings.json - Use an existing configuration file
  2. Use .NET environment variables - Override specific settings (e.g., ConnectionStrings__DefaultConnection)
  3. Auto-generate from custom environment variables - The container automatically creates appsettings.json from environment variables if no file is provided

Build and Run

  1. Build the Docker image:

    docker build -t tunemates-backend .
  2. Run with mounted appsettings.json:

    docker run -d \
      -p 8080:8080 \
      -v $(pwd)/appsettings.json:/app/appsettings.json:ro \
      --name tunemates-backend \
      tunemates-backend
  3. Or run with auto-generated appsettings.json from environment variables (requires CONNECTION_STRING, ENCRYPT_KEY, and JWT_KEY):

    docker run -d \
      -p 8080:8080 \
      -e CONNECTION_STRING="your_connection_string" \
      -e ENCRYPT_KEY="your_base64_key" \
      -e JWT_KEY="your_jwt_key" \
      -e JWT_ISSUER="TuneMates" \
      -e JWT_AUDIENCE="TuneMatesUsers" \
      -e SPOTIFY_CLIENT_ID="your_spotify_client_id" \
      -e SPOTIFY_CLIENT_SECRET="your_spotify_client_secret" \
      -e SPOTIFY_REDIRECT_URI="your_redirect_uri" \
      --name tunemates-backend \
      tunemates-backend

    Note: When auto-generating appsettings.json, CONNECTION_STRING, ENCRYPT_KEY, and JWT_KEY are required. The container will fail to start if these are not provided.

  4. Access the API at http://localhost:8080/api

Custom Port Configuration

The default Docker configuration uses port 8080, but you can customize this using the ASPNETCORE_URLS environment variable. For example, to use port 7016 (matching local development):

docker run -d \
  -p 7016:7016 \
  -e ASPNETCORE_URLS=http://+:7016 \
  -v $(pwd)/appsettings.json:/app/appsettings.json:ro \
  --name tunemates-backend \
  tunemates-backend

Access the API at http://localhost:7016/api

Or modify the docker-compose.yml file (update both the ports mapping and environment variable):

# In the services.backend section:
ports:
  - "7016:7016"  # host:container ports must match
environment:
  - ASPNETCORE_URLS=http://+:7016  # must match container port above

Using Docker Compose

A docker-compose.yml file is included in the repository for convenient deployment.

  1. Copy the environment template:

    cp docker-compose.env.example .env
  2. Edit .env with your configuration values

  3. Run:

    docker-compose up -d

See docker-compose.env.example for all available environment variables.

Appsettings

Make sure to configure (maybe even create) your appsettings.json file with the necessary settings, such as database connection strings and JWT secret keys.

You can find a sample configuration in appsettings.sample.json.

Example

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "supabase connection string here"
  },
  "Secrets": {
    "EncryptKey64": "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFG="
  },
  "Jwt": {
    "Key": "don't worry about it, this is a good key at least i hope so",
    "Issuer": "TuneMates",
    "Audience": "TuneMatesUsers",
    "ExpiresInMinutes": 180 // 3 hours
  },
  "CleanupService": {
    "TokenIntervalHours": 3,
    "ProposalIntervalHours": 3,
    "RoomIntervalHours": 24,
    "SpotifyStateIntervalHours": 1,
    "RoomCodeIntervalHours": 24
  },
  "Cors": {
    "AllowedOrigins": [
      "http://localhost:5173",
      "https://localhost:5173"
    ],
    "AllowCredentials": true
  },
  "RateLimiting": {
    "GlobalPerMinute": 0, // 0 means disabled
    "SearchPerMinute": 30,
    "MutationPerMinute":  10
  },
  "Spotify": {
    "ClientId": "your_spotify_client_id",
    "ClientSecret": "your_spotify_client_secret",
    "RedirectUri": "your_redirect_uri"
  }
}

The ConnectionStrings:DefaultConnection should be your database connection string.

The Secrets:EncryptKey64 should be a base64-encoded string of 64 characters.

The Jwt:Key should be a strong secret key used for signing JWT tokens.

The Jwt:Issuer and Jwt:Audience should be set to appropriate values for your application.

The CleanupService section contains settings for background services that clean up expired tokens, proposals, etc. You can adjust the intervals as needed (in hours).

The Cors section contains settings for Cross-Origin Resource Sharing (CORS). You can specify the allowed origins and whether to allow credentials.

The RateLimiting section contains settings for rate limiting API requests. You can specify the number of allowed requests per minute for different categories (global, search, mutation).

The Spotify section should contain your Spotify API credentials.

Tip

Spotify Developer Account

To use Spotify's API, you need to create a Spotify Developer account and register your application to obtain the ClientId and ClientSecret. You can do this at Spotify for Developers.

JWT Authentication

The application uses JWT (JSON Web Tokens) for authentication. After a user logs in, they will receive a JWT token that must be included in the Authorization header of subsequent requests to protected endpoints. The token should be prefixed with Bearer .

Example

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

There has to be a space between Bearer and the token.

Database

The application uses Entity Framework Core for database management.

The database schema includes tables for Users, Rooms, Songs, and Tokens.

You can find the queries to create the necessary tables in the database.sql file.

Warning

PostgreSQL and Supabase

The provided SQL queries are designed for PostgreSQL, which is the database system used by Supabase. If you are using a different database system, you may need to adjust the queries accordingly.

API Endpoints

✔️: Endpoints that require authentication.

❌: Endpoints that do not require authentication.

Users

Routes
Auth Method Endpoint Description Body Response
❌ POST /api/users/register Register a new user. None User & token
❌ POST /api/users/login Login a user. None User & token
✔️ POST /api/users/spotify/connect/{code:string}/{state:string} Connect the user's Spotify account using the provided authorization code and state. None User
✔️ POST /api/users/delete/me Delete the current user's account. None string
✔️ GET /api/users/me Get the current user's information. None User
✔️ PUT /api/users/me Update the current user's information. UserDTO User
✔️ PUT /api/users/me/password Update the current user's password. UserDTO User

Rooms

Routes
Auth Method Endpoint Description Body Response
✔️ POST /api/rooms Create a new room. None Room
❌ POST /api/rooms/code Get details of a specific room using a code. No password needed RoomCode (code) Room
✔️ POST /api/rooms/code/{id:int} Create and get the code to access the room easily. No password required if you have the code of a room. RoomCodeDTO An object with the code and the expiracy date.
❌ POST /api/rooms/slug/{slug:string} Get details of a specific room by its slug. Requires room password RoomDTO Room
✔️ POST /api/rooms/delete/code Delete a code. RoomCode string
✔️ GET /api/rooms Get a list of all rooms from the authenticated user. None List of Room
✔️ GET /api/rooms/{id:int} Get a list of all rooms from the authenticated user. None List of Room
✔️ GET /api/rooms/codes/{id:int} Get a list of all the codes for the room None List of RoomCode
✔️ PUT /api/rooms/{id:int} Update details of a specific room by its id. RoomDTO Room
✔️ PUT /api/rooms/password/{id:int} Update details of a specific room's password by its id. RoomDTO Room
✔️ DELETE /api/rooms/{id:int} Delete a room by its id. None string

Songs

Routes
Auth Method Endpoint Description Body Response
❌ POST /api/songs/room/{roomId:int}/{songId?:string} Add a new song to a room using a Spotify song ID or the Track URI or URL (in body). The song will be in "Pending" status by default. Song? Song
✔️ GET /api/songs/room/{roomId:int} Get all songs in a specific room. None List of Song
✔️ GET /api/songs/room/{roomId:int}/status/{status:int} Get songs in a specific room by their status (SongStatus Pending: 0, Approved: 1, Refused: 2). None List of Song

Spotify

Routes
Auth Method Endpoint Description Body Response
✔️ POST /api/spotify/playlist/{roomId:int}/{songId:int} Add a new song directly to the room's Spotify playlist using the songId from the database. The song will be in Approved status after being added to the playlist. None Song & snapshot id
✔️ GET /api/spotify/token Get the Spotify access token for the authenticated user. None string
❌ GET /api/spotify/oathlink Get the Spotify OAuth link to authorize the application. None string
✔️ GET /api/spotify/playlist/me Get the authenticated user, all their playlists from Spotify. None Playlist Response
❌ GET /api/spotify/search/{q:string}/{offset:int}/{market:string} Search for songs on Spotify by a query string, with pagination and market specification. None PageResult Response

About

A .NET 9 Web API that powers collaborative Spotify playlists. Handles Spotify OAuth, room management, and track proposals, with JWT authentication for owners. Uses PostgreSQL (via Supabase) for persistence. Provides secure endpoints for creating rooms, proposing tracks, and syncing approved songs into Spotify playlists.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages