diff --git a/API_DOCUMENTATION.md b/API_DOCUMENTATION.md deleted file mode 100644 index 7e925f1..0000000 --- a/API_DOCUMENTATION.md +++ /dev/null @@ -1,220 +0,0 @@ -# SERENA API - -## Authentication - -This system uses JWT-like tokens generated automatically during key actions: - -- **Creating a radio station** generates an **owner token**. -- **Joining a radio station** generates a **client token**. - -All subsequent requests require this token in the `Authorization` header: - -``` -Authorization: Bearer -``` - -### Token Generation Endpoints - -#### Create Radio Station → Owner Token - -- **POST** `/api/radio-stations` -- **No authentication required** -- **Body**: - -```json -{ - "name": "My Radio Station", - "description": "A cool radio station" -} -``` - -**Response:** - -```json -{ - "success": true, - "message": "Radio station created successfully", - "data": { - "station": { - /* station object */ - }, - "ownerToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } -} -``` - -#### Connect to Radio Station → Client Token - -- **POST** `/api/clients/connect` -- **No authentication required** -- **Body Options**: - - - With Station ID + Join Code - - ```json - { - "username": "john_doe", - "radioStationId": "station123", - "joinCode": "ABC123" - } - ``` - - - With Join Code only - - ```json - { "username": "john_doe", "joinCode": "ABC123" } - ``` - -**Response:** - -```json -{ - "success": true, - "message": "Successfully connected to radio station", - "data": { - "client": { - /* client object */ - }, - "clientToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } -} -``` - ---- - -## API Endpoints - -### Radio Station Management - -- **Create Station** – `POST /api/radio-stations` (No auth; returns owner token) -- **Get All Stations** – `GET /api/radio-stations` (Auth: owner or client) - - - Optional query param: `activeOnly=true` - -- **Get by ID** – `GET /api/radio-stations/{stationId}` (Auth required) -- **Get by Join Code** – `GET /api/radio-stations/join/{joinCode}` (Public) -- **Update Station** – `PUT /api/radio-stations/{stationId}` (Owner only) -- **Delete Station** – `DELETE /api/radio-stations/{stationId}` (Owner only) - -### Client Management - -- **Connect Client** – `POST /api/clients/connect` (No auth; returns client token) -- **Disconnect Client** – `DELETE /api/clients/{clientId}/disconnect` (Owner only) -- **Get Client Info** – `GET /api/clients/{clientId}` (Auth required) -- **Get Station Clients** – `GET /api/clients/station/{radioStationId}` (Auth required) - -### Song Management - -> All endpoints require a valid **owner** or **client** token unless specified. - -- **Add Song** – `POST /api/radio-stations/{stationId}/songs` -- **Get Queue** – `GET /api/radio-stations/{stationId}/songs/queue` -- **Get Current Song** – `GET /api/radio-stations/{stationId}/songs/current` -- **Play Next Song** – `POST /api/radio-stations/{stationId}/songs/next` (Owner only) -- **Vote on Song** – `POST /api/radio-stations/{stationId}/songs/{songId}/vote` - - ```json - { "voteType": "UPVOTE" } - ``` - - `voteType` can be `"UPVOTE"` or `"DOWNVOTE"`. - -- **Remove Vote** – `DELETE /api/radio-stations/{stationId}/songs/{songId}/vote` - ---- - -## Models - -### RadioStation - -- `id`, `name`, `description`, `ownerId`, `joinCode`, `createdAt`, `isActive`, `connectedClients`, `songQueue`, `currentlyPlaying` - -### Song - -- `id`, `title`, `artist`, `album`, `duration`, `url`, `addedBy`, `addedAt`, `votes`, `upvotes`, `downvotes` - -### Client - -- `id`, `username`, `radioStationId`, `connectedAt`, `isActive` - ---- - -## Authentication & Authorization - -- **Public**: Join stations via join code, connect clients. -- **Authenticated User**: Add/vote on songs, view stations, connect to stations. -- **Station Owner**: Manage station (update/delete), control playback, disconnect clients. - ---- - -## Features - -- JWT-based authentication for most operations. -- Join code–based client connection. -- Owner-only management (updates, deletes, playback). -- Song queue sorted by votes. -- Full CORS support. - ---- - -## Example Flow - -1. User registers/logs in → gets JWT. -2. Owner creates station → gets join code + owner token. -3. Clients join via join code → get client token. -4. Authenticated users add/vote on songs. -5. Owner controls playback & manages station. - ---- - -## Running the Application - -```bash -cd backend -./gradlew bootRun -``` - -Server runs on port `8080`. - -### Quick Auth Test - -```bash -curl -X POST http://localhost:8080/api/auth/register -H "Content-Type: application/json" -d '{"username":"testuser","password":"password123","email":"test@example.com"}' -curl -X POST http://localhost:8080/api/auth/login -H "Content-Type: application/json" -d '{"username":"testuser","password":"password123"}' -curl -X GET http://localhost:8080/api/radio-stations -H "Authorization: Bearer " -``` - ---- - -### **SERENA API Endpoint Table** - -| Method | Endpoint | Description | Auth Required | Notes | -| ---------- | ------------------------------------- | ------------------------ | ------------- | -------------------------- | -| **POST** | `/api/radio-stations` | Create a radio station | ❌ | Returns **owner token** | -| **GET** | `/api/radio-stations` | Get all stations | ✅ | `activeOnly=true` optional | -| **GET** | `/api/radio-stations/{stationId}` | Get station by ID | ✅ | — | -| **GET** | `/api/radio-stations/join/{joinCode}` | Get station by join code | ❌ | Public lookup | -| **PUT** | `/api/radio-stations/{stationId}` | Update station | ✅ (Owner) | Owner only | -| **DELETE** | `/api/radio-stations/{stationId}` | Delete station | ✅ (Owner) | Owner only | - -#### 👥 **Client Management** - -| Method | Endpoint | Description | Auth Required | Notes | -| ---------- | --------------------------------------- | -------------------------- | ------------- | ------------------------ | -| **POST** | `/api/clients/connect` | Connect to station | ❌ | Returns **client token** | -| **DELETE** | `/api/clients/{clientId}/disconnect` | Disconnect client | ✅ (Owner) | Owner only | -| **GET** | `/api/clients/{clientId}` | Get client info | ✅ | — | -| **GET** | `/api/clients/station/{radioStationId}` | Get all clients in station | ✅ | — | - -#### 🎵 **Song Management** - -| Method | Endpoint | Description | Auth Required | Notes | -| ---------- | ----------------------------------------------------- | -------------------------- | ------------- | --------------------------- | -| **POST** | `/api/radio-stations/{stationId}/songs` | Add song to queue | ✅ | Client or Owner | -| **GET** | `/api/radio-stations/{stationId}/songs/queue` | Get song queue | ✅ | Sorted by votes | -| **GET** | `/api/radio-stations/{stationId}/songs/current` | Get currently playing song | ✅ | — | -| **POST** | `/api/radio-stations/{stationId}/songs/next` | Play next song | ✅ (Owner) | Moves top song to “current” | -| **POST** | `/api/radio-stations/{stationId}/songs/{songId}/vote` | Vote on song | ✅ | `voteType`: UPVOTE/DOWNVOTE | -| **DELETE** | `/api/radio-stations/{stationId}/songs/{songId}/vote` | Remove vote | ✅ | Removes user’s vote | - ---- diff --git a/backend/API_DOCUMENTATION.md b/backend/API_DOCUMENTATION.md new file mode 100644 index 0000000..0617740 --- /dev/null +++ b/backend/API_DOCUMENTATION.md @@ -0,0 +1,578 @@ +# Serena API docs + +## Overview + +**Base URL:** `http://localhost:8080/api` + +## Authentication + +The API uses Bearer token authentication. Include the token in the Authorization header: + +``` +Authorization: Bearer +``` + +### Token Types + +- **Owner Token**: Generated when creating a radio station, allows full station management +- **Client Token**: Generated when joining a station, allows participation in the station + +## API Endpoints + +### Radio Station Management + +#### Create Radio Station + +Creates a new radio station and returns an owner token. + +**Endpoint:** `POST /radio-stations` + +**Request Body:** + +```json +{ + "name": "My Awesome Station", + "description": "The best music station ever" +} +``` + +**Response:** + +```json +{ + "success": true, + "message": "Radio station created successfully", + "data": { + "station": { + "id": "station-uuid", + "name": "My Awesome Station", + "description": "The best music station ever", + "ownerId": "owner-uuid", + "joinCode": "ABC123", + "createdAt": "2025-08-01T10:00:00", + "active": true, + "connectedClients": [], + "songQueue": [], + "currentlyPlaying": null + }, + "ownerToken": "jwt-token-here", + "message": "Radio station created successfully. Use this token to manage your station." + } +} +``` + +#### Get All Radio Stations + +Retrieves all radio stations. Requires authentication. + +**Endpoint:** `GET /radio-stations?activeOnly=true` + +**Query Parameters:** + +- `activeOnly` (boolean, default: false): Filter to only active stations + +**Response:** + +```json +{ + "success": true, + "message": "Success", + "data": [ + { + "id": "station-uuid", + "name": "Station Name", + "description": "Station Description", + "ownerId": "owner-uuid", + "joinCode": "ABC123", + "createdAt": "2025-08-01T10:00:00", + "active": true, + "connectedClients": ["client1", "client2"], + "songQueue": [], + "currentlyPlaying": null + } + ] +} +``` + +#### Get Radio Station by ID + +Retrieves a specific radio station. Requires authentication. + +**Endpoint:** `GET /radio-stations/{stationId}` + +**Response:** + +```json +{ + "success": true, + "message": "Success", + "data": { + "id": "station-uuid", + "name": "Station Name", + "description": "Station Description", + "ownerId": "owner-uuid", + "joinCode": "ABC123", + "createdAt": "2025-08-01T10:00:00", + "active": true, + "connectedClients": [], + "songQueue": [], + "currentlyPlaying": null + } +} +``` + +#### Get Radio Station by Join Code + +Retrieves a radio station using its join code. No authentication required. + +**Endpoint:** `GET /radio-stations/join/{joinCode}` + +**Response:** + +```json +{ + "success": true, + "message": "Success", + "data": { + "id": "station-uuid", + "name": "Station Name", + "description": "Station Description", + "ownerId": "owner-uuid", + "joinCode": "ABC123", + "createdAt": "2025-08-01T10:00:00", + "active": true, + "connectedClients": [], + "songQueue": [], + "currentlyPlaying": null + } +} +``` + +#### Update Radio Station + +Updates a radio station. Only the station owner can update it. + +**Endpoint:** `PUT /radio-stations/{stationId}` + +**Request Body:** + +```json +{ + "name": "Updated Station Name", + "description": "Updated description" +} +``` + +**Response:** + +```json +{ + "success": true, + "message": "Radio station updated successfully", + "data": { + "id": "station-uuid", + "name": "Updated Station Name", + "description": "Updated description", + "ownerId": "owner-uuid", + "joinCode": "ABC123", + "createdAt": "2025-08-01T10:00:00", + "active": true, + "connectedClients": [], + "songQueue": [], + "currentlyPlaying": null + } +} +``` + +#### Delete Radio Station + +Deletes a radio station. Only the station owner can delete it. + +**Endpoint:** `DELETE /radio-stations/{stationId}` + +**Response:** + +```json +{ + "success": true, + "message": "Radio station deleted successfully", + "data": null +} +``` + +### Client Management + +#### Connect Client to Station + +Connects a client to a radio station using a join code. No authentication required. + +**Endpoint:** `POST /clients/connect` + +**Request Body (Option 1 - Using station ID and join code):** + +```json +{ + "username": "john_doe", + "radioStationId": "station-uuid", + "joinCode": "ABC123" +} +``` + +**Request Body (Option 2 - Using join code only):** + +```json +{ + "username": "john_doe", + "joinCode": "ABC123" +} +``` + +**Response:** + +```json +{ + "success": true, + "message": "Successfully connected to radio station", + "data": { + "client": { + "id": "client-uuid", + "username": "john_doe", + "radioStationId": "station-uuid", + "connectedAt": "2025-08-01T10:00:00", + "active": true + }, + "clientToken": "jwt-token-here", + "message": "Successfully connected to radio station. Use this token for further requests." + } +} +``` + +#### Disconnect Client + +Disconnects a client from a station. Only the station owner can disconnect clients. + +**Endpoint:** `DELETE /clients/{clientId}/disconnect` + +**Response:** + +```json +{ + "success": true, + "message": "Client disconnected successfully", + "data": null +} +``` + +#### Get Client Information + +Retrieves information about a specific client. Requires authentication. + +**Endpoint:** `GET /clients/{clientId}` + +**Response:** + +```json +{ + "success": true, + "message": "Success", + "data": { + "id": "client-uuid", + "username": "john_doe", + "radioStationId": "station-uuid", + "connectedAt": "2025-08-01T10:00:00", + "active": true + } +} +``` + +#### Get Connected Clients + +Retrieves all clients connected to a radio station. Requires authentication. + +**Endpoint:** `GET /clients/station/{radioStationId}` + +**Response:** + +```json +{ + "success": true, + "message": "Success", + "data": [ + { + "id": "client-uuid", + "username": "john_doe", + "radioStationId": "station-uuid", + "connectedAt": "2025-08-01T10:00:00", + "active": true + } + ] +} +``` + +### Song Management + +#### Add Song to Queue + +Adds a song to the station's queue. Requires authentication. + +**Endpoint:** `POST /radio-stations/{stationId}/songs` + +**Request Body:** + +```json +{ + "title": "Bohemian Rhapsody", + "artist": "Queen", + "album": "A Night at the Opera", + "duration": 355, + "url": "https://example.com/song.mp3" +} +``` + +**Response:** + +```json +{ + "success": true, + "message": "Song added to queue successfully", + "data": { + "id": "song-uuid", + "title": "Bohemian Rhapsody", + "artist": "Queen", + "album": "A Night at the Opera", + "duration": 355, + "url": "https://example.com/song.mp3", + "addedBy": "user-uuid", + "addedAt": "2025-08-01T10:00:00", + "votes": {}, + "upvotes": 0, + "downvotes": 0 + } +} +``` + +#### Get Song Queue + +Retrieves the current song queue, sorted by score (upvotes - downvotes). Requires authentication. + +**Endpoint:** `GET /radio-stations/{stationId}/songs/queue` + +**Response:** + +```json +{ + "success": true, + "message": "Success", + "data": [ + { + "id": "song-uuid", + "title": "Bohemian Rhapsody", + "artist": "Queen", + "album": "A Night at the Opera", + "duration": 355, + "url": "https://example.com/song.mp3", + "addedBy": "user-uuid", + "addedAt": "2025-08-01T10:00:00", + "votes": { + "user1": "UPVOTE", + "user2": "DOWNVOTE" + }, + "upvotes": 1, + "downvotes": 1 + } + ] +} +``` + +#### Get Currently Playing Song + +Retrieves the currently playing song. Requires authentication. + +**Endpoint:** `GET /radio-stations/{stationId}/songs/current` + +**Response:** + +```json +{ + "success": true, + "message": "Success", + "data": { + "id": "song-uuid", + "title": "Bohemian Rhapsody", + "artist": "Queen", + "album": "A Night at the Opera", + "duration": 355, + "url": "https://example.com/song.mp3", + "addedBy": "user-uuid", + "addedAt": "2025-08-01T10:00:00", + "votes": {}, + "upvotes": 0, + "downvotes": 0 + } +} +``` + +#### Play Next Song + +Plays the next song from the queue (highest scoring song). Only station owners can control playback. + +**Endpoint:** `POST /radio-stations/{stationId}/songs/next` + +**Response:** + +```json +{ + "success": true, + "message": "Playing next song", + "data": { + "id": "song-uuid", + "title": "Bohemian Rhapsody", + "artist": "Queen", + "album": "A Night at the Opera", + "duration": 355, + "url": "https://example.com/song.mp3", + "addedBy": "user-uuid", + "addedAt": "2025-08-01T10:00:00", + "votes": {}, + "upvotes": 0, + "downvotes": 0 + } +} +``` + +#### Vote on Song + +Casts a vote (upvote or downvote) on a song. Requires authentication. + +**Endpoint:** `POST /radio-stations/{stationId}/songs/{songId}/vote` + +**Request Body:** + +```json +{ + "voteType": "UPVOTE" +} +``` + +**Vote Types:** + +- `UPVOTE`: Positive vote +- `DOWNVOTE`: Negative vote + +**Response:** + +```json +{ + "success": true, + "message": "Vote recorded successfully", + "data": { + "id": "song-uuid", + "title": "Bohemian Rhapsody", + "artist": "Queen", + "album": "A Night at the Opera", + "duration": 355, + "url": "https://example.com/song.mp3", + "addedBy": "user-uuid", + "addedAt": "2025-08-01T10:00:00", + "votes": { + "current-user-id": "UPVOTE" + }, + "upvotes": 1, + "downvotes": 0 + } +} +``` + +#### Remove Vote from Song + +Removes the current user's vote from a song. Requires authentication. + +**Endpoint:** `DELETE /radio-stations/{stationId}/songs/{songId}/vote` + +**Response:** + +```json +{ + "success": true, + "message": "Vote removed successfully", + "data": null +} +``` + +## Error Responses + +All error responses follow this format: + +```json +{ + "success": false, + "message": "Error description", + "data": null +} +``` + +### Common HTTP Status Codes + +- `200 OK`: Request successful +- `201 Created`: Resource created successfully +- `400 Bad Request`: Invalid request data +- `401 Unauthorized`: Authentication required or token invalid +- `403 Forbidden`: Access denied (e.g., not station owner) +- `404 Not Found`: Resource not found + +### Common Error Messages + +- `"User not authenticated"`: Missing or invalid authentication token +- `"Radio station not found"`: Station ID or join code not found +- `"Only the station owner can..."`: Action requires owner privileges +- `"Failed to connect to radio station. Invalid join code or station not found."`: Join code is invalid +- `"Client not found"`: Client ID not found + +## Usage Examples + +### Creating a Station and Adding Songs + +1. **Create a station:** + +```bash +curl -X POST http://localhost:8080/api/radio-stations \ + -H "Content-Type: application/json" \ + -d '{"name": "My Station", "description": "Great music"}' +``` + +2. **Use the returned owner token for authenticated requests:** + +```bash +curl -X POST http://localhost:8080/api/radio-stations/{stationId}/songs \ + -H "Authorization: Bearer {ownerToken}" \ + -H "Content-Type: application/json" \ + -d '{"title": "Song Title", "artist": "Artist", "duration": 180, "url": "http://example.com/song.mp3"}' +``` + +### Joining a Station and Voting + +1. **Join a station:** + +```bash +curl -X POST http://localhost:8080/api/clients/connect \ + -H "Content-Type: application/json" \ + -d '{"username": "john", "joinCode": "ABC123"}' +``` + +2. **Vote on a song:** + +```bash +curl -X POST http://localhost:8080/api/radio-stations/{stationId}/songs/{songId}/vote \ + -H "Authorization: Bearer {clientToken}" \ + -H "Content-Type: application/json" \ + -d '{"voteType": "UPVOTE"}' +``` + +## Notes + +- Join codes are 6-character alphanumeric strings automatically generated for each station +- Songs in the queue are sorted by score (upvotes - downvotes) in descending order +- Users can change their vote on a song, but only have one vote per song +- Only station owners can control playback (play next song) +- Tokens expire after 7 days +- The system uses in-memory storage, so data is lost when the server restarts diff --git a/backend/build.gradle b/backend/build.gradle index b670cd6..b098bfa 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -4,7 +4,7 @@ plugins { id 'io.spring.dependency-management' version '1.1.7' } -group = 'com.hackathon' +group = 'com.serena' version = '0.0.1-SNAPSHOT' java { diff --git a/backend/settings.gradle b/backend/settings.gradle index 0f5036d..1929111 100644 --- a/backend/settings.gradle +++ b/backend/settings.gradle @@ -1 +1 @@ -rootProject.name = 'backend' +rootProject.name = 'serena_backend' diff --git a/backend/src/main/java/com/hackathon/backend/config/JwtAuthenticationFilter.java b/backend/src/main/java/com/hackathon/backend/config/JwtAuthenticationFilter.java deleted file mode 100644 index 733564f..0000000 --- a/backend/src/main/java/com/hackathon/backend/config/JwtAuthenticationFilter.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.hackathon.backend.config; - -import com.hackathon.backend.service.JwtService; -import com.hackathon.backend.service.UserService; -import com.hackathon.backend.model.User; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.lang.NonNull; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Optional; - -@Component -public class JwtAuthenticationFilter extends OncePerRequestFilter { - - @Autowired - private JwtService jwtService; - - @Autowired - private UserService userService; - - @Override - protected void doFilterInternal( - @NonNull HttpServletRequest request, - @NonNull HttpServletResponse response, - @NonNull FilterChain filterChain) throws ServletException, IOException { - - final String authHeader = request.getHeader("Authorization"); - final String jwt; - final String userId; - - if (authHeader == null || !authHeader.startsWith("Bearer ")) { - filterChain.doFilter(request, response); - return; - } - - jwt = authHeader.substring(7); - userId = jwtService.extractUserId(jwt); - - if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) { - Optional userOpt = userService.getUserById(userId); - - if (userOpt.isPresent()) { - User user = userOpt.get(); - - if (jwtService.isTokenValid(jwt, userId) && user.isActive()) { - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - user, - null, - new ArrayList<>()); - authToken.setDetails( - new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authToken); - } - } - } - - filterChain.doFilter(request, response); - } -} diff --git a/backend/src/main/java/com/hackathon/backend/controller/AuthController.java b/backend/src/main/java/com/hackathon/backend/controller/AuthController.java deleted file mode 100644 index e3647cd..0000000 --- a/backend/src/main/java/com/hackathon/backend/controller/AuthController.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.hackathon.backend.controller; - -import com.hackathon.backend.dto.ApiResponse; -import com.hackathon.backend.dto.AuthResponse; -import com.hackathon.backend.dto.LoginRequest; -import com.hackathon.backend.dto.RegisterRequest; -import com.hackathon.backend.model.User; -import com.hackathon.backend.service.JwtService; -import com.hackathon.backend.service.UserService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.Optional; - -@RestController -@RequestMapping("/api/auth") -@CrossOrigin(origins = "*") -public class AuthController { - - @Autowired - private UserService userService; - - @Autowired - private JwtService jwtService; - - @PostMapping("/register") - public ResponseEntity> register(@RequestBody RegisterRequest request) { - if (request.getUsername() == null || request.getUsername().trim().isEmpty()) { - return ResponseEntity.badRequest() - .body(ApiResponse.error("Username is required")); - } - - if (request.getPassword() == null || request.getPassword().length() < 6) { - return ResponseEntity.badRequest() - .body(ApiResponse.error("Password must be at least 6 characters long")); - } - - if (request.getEmail() == null || request.getEmail().trim().isEmpty()) { - return ResponseEntity.badRequest() - .body(ApiResponse.error("Email is required")); - } - - Optional userOpt = userService.registerUser( - request.getUsername().trim(), - request.getPassword(), - request.getEmail().trim()); - - if (userOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.CONFLICT) - .body(ApiResponse.error("Username already exists")); - } - - User user = userOpt.get(); - String token = jwtService.generateToken(user.getId(), user.getUsername()); - - AuthResponse authResponse = new AuthResponse( - token, - user.getId(), - user.getUsername(), - jwtService.getExpirationTime()); - - return ResponseEntity.status(HttpStatus.CREATED) - .body(ApiResponse.success("User registered successfully", authResponse)); - } - - @PostMapping("/login") - public ResponseEntity> login(@RequestBody LoginRequest request) { - if (request.getUsername() == null || request.getUsername().trim().isEmpty()) { - return ResponseEntity.badRequest() - .body(ApiResponse.error("Username is required")); - } - - if (request.getPassword() == null || request.getPassword().isEmpty()) { - return ResponseEntity.badRequest() - .body(ApiResponse.error("Password is required")); - } - - Optional userOpt = userService.authenticateUser( - request.getUsername().trim(), - request.getPassword()); - - if (userOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("Invalid username or password")); - } - - User user = userOpt.get(); - String token = jwtService.generateToken(user.getId(), user.getUsername()); - - AuthResponse authResponse = new AuthResponse( - token, - user.getId(), - user.getUsername(), - jwtService.getExpirationTime()); - - return ResponseEntity.ok(ApiResponse.success("Login successful", authResponse)); - } - - @GetMapping("/me") - public ResponseEntity> getCurrentUser(@RequestHeader("Authorization") String authHeader) { - try { - String token = authHeader.substring(7); // Remove "Bearer " - String userId = jwtService.extractUserId(token); - - Optional userOpt = userService.getUserById(userId); - if (userOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("User not found")); - } - - User user = userOpt.get(); - // Don't send password in response - user.setPassword(null); - - return ResponseEntity.ok(ApiResponse.success(user)); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("Invalid token")); - } - } -} diff --git a/backend/src/main/java/com/hackathon/backend/controller/ClientController.java b/backend/src/main/java/com/hackathon/backend/controller/ClientController.java deleted file mode 100644 index b6d6be2..0000000 --- a/backend/src/main/java/com/hackathon/backend/controller/ClientController.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.hackathon.backend.controller; - -import com.hackathon.backend.dto.ApiResponse; -import com.hackathon.backend.dto.ConnectClientRequest; -import com.hackathon.backend.model.Client; -import com.hackathon.backend.model.RadioStation; -import com.hackathon.backend.service.RadioStationService; -import com.hackathon.backend.service.SimpleTokenService; -import com.hackathon.backend.util.AuthUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -@RestController -@RequestMapping("/api/clients") -@CrossOrigin(origins = "*") -public class ClientController { - - @Autowired - private RadioStationService radioStationService; - - @Autowired - private SimpleTokenService tokenService; - - @PostMapping("/connect") - public ResponseEntity>> connectClient(@RequestBody ConnectClientRequest request) { - // This endpoint is public - no authentication required for joining stations - Optional client; - - // If radioStationId is provided, use it with join code validation - if (request.getRadioStationId() != null && !request.getRadioStationId().isEmpty()) { - client = radioStationService.connectClient( - request.getUsername(), - request.getRadioStationId(), - request.getJoinCode()); - } else { - // Otherwise, connect by join code only - client = radioStationService.connectClientByJoinCode( - request.getUsername(), - request.getJoinCode()); - } - - if (client.isPresent()) { - // Generate a client token - String clientToken = tokenService.generateToken(request.getUsername(), "client"); - - Map response = new HashMap<>(); - response.put("client", client.get()); - response.put("clientToken", clientToken); - response.put("message", "Successfully connected to radio station. Use this token for further requests."); - - return ResponseEntity.status(HttpStatus.CREATED) - .body(ApiResponse.success("Successfully connected to radio station", response)); - } else { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(ApiResponse.error("Failed to connect to radio station. Invalid join code or station not found.")); - } - } - - @DeleteMapping("/{clientId}/disconnect") - public ResponseEntity> disconnectClient(@PathVariable String clientId) { - String currentUserId = AuthUtil.getCurrentUserId(); - - // Check if user is authenticated - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - // Get client to check authorization - Optional clientOpt = radioStationService.getClient(clientId); - if (clientOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Client not found")); - } - - Client client = clientOpt.get(); - - // Get the station to check if current user is the owner - Optional stationOpt = radioStationService.getRadioStation(client.getRadioStationId()); - if (stationOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Radio station not found")); - } - - RadioStation station = stationOpt.get(); - - // Allow disconnection if: - // 1. Current user is the station owner (can disconnect anyone) - // 2. Current user is the client themselves (self-disconnect) - boolean isOwner = currentUserId.equals(station.getOwnerId()); - // Note: For self-disconnect, we'd need to link clients to users, which isn't - // implemented yet - // For now, only station owners can disconnect clients - - if (!isOwner) { - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body(ApiResponse.error("Only the station owner can disconnect clients")); - } - - boolean disconnected = radioStationService.disconnectClient(clientId); - if (disconnected) { - return ResponseEntity.ok(ApiResponse.success("Client disconnected successfully", null)); - } else { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(ApiResponse.error("Failed to disconnect client")); - } - } - - @GetMapping("/{clientId}") - public ResponseEntity> getClient(@PathVariable String clientId) { - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - Optional client = radioStationService.getClient(clientId); - if (client.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Client not found")); - } - - return ResponseEntity.ok(ApiResponse.success(client.get())); - } - - @GetMapping("/station/{radioStationId}") - public ResponseEntity>> getConnectedClients(@PathVariable String radioStationId) { - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - // Check if station exists - Optional stationOpt = radioStationService.getRadioStation(radioStationId); - if (stationOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Radio station not found")); - } - - List clients = radioStationService.getConnectedClients(radioStationId); - return ResponseEntity.ok(ApiResponse.success(clients)); - } -} diff --git a/backend/src/main/java/com/hackathon/backend/controller/SongController.java b/backend/src/main/java/com/hackathon/backend/controller/SongController.java deleted file mode 100644 index 2923819..0000000 --- a/backend/src/main/java/com/hackathon/backend/controller/SongController.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.hackathon.backend.controller; - -import com.hackathon.backend.dto.AddSongRequest; -import com.hackathon.backend.dto.ApiResponse; -import com.hackathon.backend.dto.VoteRequest; -import com.hackathon.backend.model.RadioStation; -import com.hackathon.backend.model.Song; -import com.hackathon.backend.service.RadioStationService; -import com.hackathon.backend.util.AuthUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; -import java.util.Optional; - -@RestController -@RequestMapping("/api/radio-stations/{stationId}/songs") -@CrossOrigin(origins = "*") -public class SongController { - - @Autowired - private RadioStationService radioStationService; - - @PostMapping - public ResponseEntity> addSongToQueue( - @PathVariable String stationId, - @RequestBody AddSongRequest request) { - - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - // Check if station exists - Optional stationOpt = radioStationService.getRadioStation(stationId); - if (stationOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Radio station not found")); - } - - // Use the authenticated user as the one adding the song - Optional song = radioStationService.addSongToQueue( - stationId, - request.getTitle(), - request.getArtist(), - request.getAlbum(), - request.getDuration(), - request.getUrl(), - currentUserId); - - if (song.isPresent()) { - return ResponseEntity.status(HttpStatus.CREATED) - .body(ApiResponse.success("Song added to queue successfully", song.get())); - } else { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(ApiResponse.error("Failed to add song to queue")); - } - } - - @GetMapping("/queue") - public ResponseEntity>> getSongQueue(@PathVariable String stationId) { - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - List queue = radioStationService.getSongQueue(stationId); - return ResponseEntity.ok(ApiResponse.success(queue)); - } - - @GetMapping("/current") - public ResponseEntity> getCurrentlyPlaying(@PathVariable String stationId) { - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - Optional current = radioStationService.getCurrentlyPlaying(stationId); - if (current.isPresent()) { - return ResponseEntity.ok(ApiResponse.success(current.get())); - } else { - return ResponseEntity.ok(ApiResponse.success("No song currently playing", null)); - } - } - - @PostMapping("/next") - public ResponseEntity> playNextSong(@PathVariable String stationId) { - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - // Check if station exists - Optional stationOpt = radioStationService.getRadioStation(stationId); - if (stationOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Radio station not found")); - } - - RadioStation station = stationOpt.get(); - - // Only station owner can control playback - if (!currentUserId.equals(station.getOwnerId())) { - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body(ApiResponse.error("Only the station owner can control playback")); - } - - Optional nextSong = radioStationService.playNextSong(stationId); - if (nextSong.isPresent()) { - return ResponseEntity.ok(ApiResponse.success("Playing next song", nextSong.get())); - } else { - return ResponseEntity.ok(ApiResponse.success("No songs in queue", null)); - } - } - - @PostMapping("/{songId}/vote") - public ResponseEntity> voteSong( - @PathVariable String stationId, - @PathVariable String songId, - @RequestBody VoteRequest request) { - - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - // Use the authenticated user as the one voting - Optional song = radioStationService.voteSong( - stationId, - songId, - currentUserId, - request.getVoteType()); - - if (song.isPresent()) { - return ResponseEntity.ok(ApiResponse.success("Vote recorded successfully", song.get())); - } else { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(ApiResponse.error("Failed to record vote")); - } - } - - @DeleteMapping("/{songId}/vote") - public ResponseEntity> removeSongVote( - @PathVariable String stationId, - @PathVariable String songId) { - - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - boolean removed = radioStationService.removeSongVote(stationId, songId, currentUserId); - if (removed) { - return ResponseEntity.ok(ApiResponse.success("Vote removed successfully", null)); - } else { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(ApiResponse.error("Failed to remove vote")); - } - } -} diff --git a/backend/src/main/java/com/hackathon/backend/dto/AddSongRequest.java b/backend/src/main/java/com/hackathon/backend/dto/AddSongRequest.java deleted file mode 100644 index c925d2a..0000000 --- a/backend/src/main/java/com/hackathon/backend/dto/AddSongRequest.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.hackathon.backend.dto; - -public class AddSongRequest { - private String title; - private String artist; - private String album; - private int duration; - private String url; - private String addedBy; - - public AddSongRequest() { - } - - public AddSongRequest(String title, String artist, String album, int duration, String url, String addedBy) { - this.title = title; - this.artist = artist; - this.album = album; - this.duration = duration; - this.url = url; - this.addedBy = addedBy; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getArtist() { - return artist; - } - - public void setArtist(String artist) { - this.artist = artist; - } - - public String getAlbum() { - return album; - } - - public void setAlbum(String album) { - this.album = album; - } - - public int getDuration() { - return duration; - } - - public void setDuration(int duration) { - this.duration = duration; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getAddedBy() { - return addedBy; - } - - public void setAddedBy(String addedBy) { - this.addedBy = addedBy; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/dto/LoginRequest.java b/backend/src/main/java/com/hackathon/backend/dto/LoginRequest.java deleted file mode 100644 index b5cfd4b..0000000 --- a/backend/src/main/java/com/hackathon/backend/dto/LoginRequest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.hackathon.backend.dto; - -public class LoginRequest { - private String username; - private String password; - - public LoginRequest() { - } - - public LoginRequest(String username, String password) { - this.username = username; - this.password = password; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/dto/RegisterRequest.java b/backend/src/main/java/com/hackathon/backend/dto/RegisterRequest.java deleted file mode 100644 index ce65776..0000000 --- a/backend/src/main/java/com/hackathon/backend/dto/RegisterRequest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.hackathon.backend.dto; - -public class RegisterRequest { - private String username; - private String password; - private String email; - - public RegisterRequest() { - } - - public RegisterRequest(String username, String password, String email) { - this.username = username; - this.password = password; - this.email = email; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/dto/UpdateRadioStationRequest.java b/backend/src/main/java/com/hackathon/backend/dto/UpdateRadioStationRequest.java deleted file mode 100644 index 967d142..0000000 --- a/backend/src/main/java/com/hackathon/backend/dto/UpdateRadioStationRequest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.hackathon.backend.dto; - -public class UpdateRadioStationRequest { - private String name; - private String description; - - public UpdateRadioStationRequest() { - } - - public UpdateRadioStationRequest(String name, String description) { - this.name = name; - this.description = description; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/dto/VoteRequest.java b/backend/src/main/java/com/hackathon/backend/dto/VoteRequest.java deleted file mode 100644 index a236b66..0000000 --- a/backend/src/main/java/com/hackathon/backend/dto/VoteRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.hackathon.backend.dto; - -import com.hackathon.backend.model.VoteType; - -public class VoteRequest { - private String clientId; - private VoteType voteType; - - public VoteRequest() { - } - - public VoteRequest(String clientId, VoteType voteType) { - this.clientId = clientId; - this.voteType = voteType; - } - - public String getClientId() { - return clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public VoteType getVoteType() { - return voteType; - } - - public void setVoteType(VoteType voteType) { - this.voteType = voteType; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/model/Song.java b/backend/src/main/java/com/hackathon/backend/model/Song.java deleted file mode 100644 index 6504dcf..0000000 --- a/backend/src/main/java/com/hackathon/backend/model/Song.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.hackathon.backend.model; - -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class Song { - private String id; - private String title; - private String artist; - private String album; - private int duration; // in seconds - private String url; - private String addedBy; - private LocalDateTime addedAt; - private Map votes; // clientId -> vote - private int upvotes; - private int downvotes; - - public Song() { - this.id = UUID.randomUUID().toString(); - this.addedAt = LocalDateTime.now(); - this.votes = new HashMap<>(); - this.upvotes = 0; - this.downvotes = 0; - } - - public Song(String title, String artist, String album, int duration, String url, String addedBy) { - this(); - this.title = title; - this.artist = artist; - this.album = album; - this.duration = duration; - this.url = url; - this.addedBy = addedBy; - } - - public void addVote(String clientId, VoteType voteType) { - VoteType previousVote = votes.get(clientId); - - // Remove previous vote count - if (previousVote != null) { - if (previousVote == VoteType.UPVOTE) { - upvotes--; - } else if (previousVote == VoteType.DOWNVOTE) { - downvotes--; - } - } - - // Add new vote - votes.put(clientId, voteType); - if (voteType == VoteType.UPVOTE) { - upvotes++; - } else if (voteType == VoteType.DOWNVOTE) { - downvotes++; - } - } - - public void removeVote(String clientId) { - VoteType previousVote = votes.remove(clientId); - if (previousVote != null) { - if (previousVote == VoteType.UPVOTE) { - upvotes--; - } else if (previousVote == VoteType.DOWNVOTE) { - downvotes--; - } - } - } - - public int getScore() { - return upvotes - downvotes; - } - - // Getters and Setters - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getArtist() { - return artist; - } - - public void setArtist(String artist) { - this.artist = artist; - } - - public String getAlbum() { - return album; - } - - public void setAlbum(String album) { - this.album = album; - } - - public int getDuration() { - return duration; - } - - public void setDuration(int duration) { - this.duration = duration; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getAddedBy() { - return addedBy; - } - - public void setAddedBy(String addedBy) { - this.addedBy = addedBy; - } - - public LocalDateTime getAddedAt() { - return addedAt; - } - - public void setAddedAt(LocalDateTime addedAt) { - this.addedAt = addedAt; - } - - public Map getVotes() { - return votes; - } - - public void setVotes(Map votes) { - this.votes = votes; - } - - public int getUpvotes() { - return upvotes; - } - - public void setUpvotes(int upvotes) { - this.upvotes = upvotes; - } - - public int getDownvotes() { - return downvotes; - } - - public void setDownvotes(int downvotes) { - this.downvotes = downvotes; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/model/User.java b/backend/src/main/java/com/hackathon/backend/model/User.java deleted file mode 100644 index e406fad..0000000 --- a/backend/src/main/java/com/hackathon/backend/model/User.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.hackathon.backend.model; - -import java.time.LocalDateTime; -import java.util.UUID; - -public class User { - private String id; - private String username; - private String password; // This would be hashed in a real application - private String email; - private LocalDateTime createdAt; - private boolean isActive; - - public User() { - this.id = UUID.randomUUID().toString(); - this.createdAt = LocalDateTime.now(); - this.isActive = true; - } - - public User(String username, String password, String email) { - this(); - this.username = username; - this.password = password; - this.email = email; - } - - // Getters and Setters - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - - public boolean isActive() { - return isActive; - } - - public void setActive(boolean active) { - isActive = active; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/model/VoteType.java b/backend/src/main/java/com/hackathon/backend/model/VoteType.java deleted file mode 100644 index c3eac23..0000000 --- a/backend/src/main/java/com/hackathon/backend/model/VoteType.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hackathon.backend.model; - -public enum VoteType { - UPVOTE, - DOWNVOTE -} diff --git a/backend/src/main/java/com/hackathon/backend/service/SimpleTokenService.java b/backend/src/main/java/com/hackathon/backend/service/SimpleTokenService.java deleted file mode 100644 index f976565..0000000 --- a/backend/src/main/java/com/hackathon/backend/service/SimpleTokenService.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hackathon.backend.service; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import org.springframework.stereotype.Service; - -import javax.crypto.SecretKey; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.function.Function; - -@Service -public class SimpleTokenService { - - private static final String SECRET_KEY = "404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970"; - private static final long TOKEN_EXPIRATION = 7 * 24 * 60 * 60 * 1000; // 7 days - - public String generateToken(String username, String role) { - String userId = UUID.randomUUID().toString(); - Map claims = new HashMap<>(); - claims.put("username", username); - claims.put("role", role); // "owner" or "client" - - return Jwts.builder() - .claims(claims) - .subject(userId) - .issuedAt(new Date(System.currentTimeMillis())) - .expiration(new Date(System.currentTimeMillis() + TOKEN_EXPIRATION)) - .signWith(getSignInKey()) - .compact(); - } - - public String extractUserId(String token) { - return extractClaim(token, Claims::getSubject); - } - - public String extractUsername(String token) { - return extractClaim(token, claims -> claims.get("username", String.class)); - } - - public String extractRole(String token) { - return extractClaim(token, claims -> claims.get("role", String.class)); - } - - public T extractClaim(String token, Function claimsResolver) { - final Claims claims = extractAllClaims(token); - return claimsResolver.apply(claims); - } - - public boolean isTokenValid(String token) { - try { - return !isTokenExpired(token); - } catch (Exception e) { - return false; - } - } - - private boolean isTokenExpired(String token) { - return extractExpiration(token).before(new Date()); - } - - private Date extractExpiration(String token) { - return extractClaim(token, Claims::getExpiration); - } - - private Claims extractAllClaims(String token) { - return Jwts.parser() - .verifyWith(getSignInKey()) - .build() - .parseSignedClaims(token) - .getPayload(); - } - - private SecretKey getSignInKey() { - byte[] keyBytes = SECRET_KEY.getBytes(); - return Keys.hmacShaKeyFor(keyBytes); - } - - public long getExpirationTime() { - return TOKEN_EXPIRATION; - } -} diff --git a/backend/src/main/java/com/hackathon/backend/service/UserService.java b/backend/src/main/java/com/hackathon/backend/service/UserService.java deleted file mode 100644 index 7f0811c..0000000 --- a/backend/src/main/java/com/hackathon/backend/service/UserService.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hackathon.backend.service; - -import com.hackathon.backend.model.User; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -@Service -public class UserService { - - private final Map users = new HashMap<>(); - private final Map usersByUsername = new HashMap<>(); - - @Autowired - private PasswordEncoder passwordEncoder; - - public Optional registerUser(String username, String password, String email) { - // Check if username already exists - if (usersByUsername.containsKey(username)) { - return Optional.empty(); - } - - // Hash the password - String hashedPassword = passwordEncoder.encode(password); - User user = new User(username, hashedPassword, email); - users.put(user.getId(), user); - usersByUsername.put(username, user.getId()); - - return Optional.of(user); - } - - public Optional authenticateUser(String username, String password) { - String userId = usersByUsername.get(username); - if (userId != null) { - User user = users.get(userId); - // Compare hashed passwords - if (user != null && passwordEncoder.matches(password, user.getPassword()) && user.isActive()) { - return Optional.of(user); - } - } - return Optional.empty(); - } - - public Optional getUserById(String userId) { - return Optional.ofNullable(users.get(userId)); - } - - public Optional getUserByUsername(String username) { - String userId = usersByUsername.get(username); - return userId != null ? Optional.ofNullable(users.get(userId)) : Optional.empty(); - } - - public boolean userExists(String username) { - return usersByUsername.containsKey(username); - } -} diff --git a/backend/src/main/java/com/hackathon/backend/BackendApplication.java b/backend/src/main/java/com/serena/backend/BackendApplication.java similarity index 90% rename from backend/src/main/java/com/hackathon/backend/BackendApplication.java rename to backend/src/main/java/com/serena/backend/BackendApplication.java index b50ad92..238fdf2 100644 --- a/backend/src/main/java/com/hackathon/backend/BackendApplication.java +++ b/backend/src/main/java/com/serena/backend/BackendApplication.java @@ -1,4 +1,4 @@ -package com.hackathon.backend; +package com.serena.backend; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/backend/src/main/java/com/hackathon/backend/config/SecurityConfig.java b/backend/src/main/java/com/serena/backend/config/SecurityConfig.java similarity index 93% rename from backend/src/main/java/com/hackathon/backend/config/SecurityConfig.java rename to backend/src/main/java/com/serena/backend/config/SecurityConfig.java index 42f6773..d9d8b04 100644 --- a/backend/src/main/java/com/hackathon/backend/config/SecurityConfig.java +++ b/backend/src/main/java/com/serena/backend/config/SecurityConfig.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.config; +package com.serena.backend.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -6,8 +6,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; diff --git a/backend/src/main/java/com/hackathon/backend/config/SimpleTokenFilter.java b/backend/src/main/java/com/serena/backend/config/SimpleTokenFilter.java similarity index 82% rename from backend/src/main/java/com/hackathon/backend/config/SimpleTokenFilter.java rename to backend/src/main/java/com/serena/backend/config/SimpleTokenFilter.java index 2db1d63..583f8ae 100644 --- a/backend/src/main/java/com/hackathon/backend/config/SimpleTokenFilter.java +++ b/backend/src/main/java/com/serena/backend/config/SimpleTokenFilter.java @@ -1,7 +1,7 @@ -package com.hackathon.backend.config; +package com.serena.backend.config; -import com.hackathon.backend.service.SimpleTokenService; -import com.hackathon.backend.model.TokenUser; +import com.serena.backend.service.JwtService; +import com.serena.backend.model.TokenUser; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -21,7 +21,7 @@ import java.util.ArrayList; public class SimpleTokenFilter extends OncePerRequestFilter { @Autowired - private SimpleTokenService tokenService; + private JwtService jwtService; @Override protected void doFilterInternal( @@ -41,10 +41,10 @@ public class SimpleTokenFilter extends OncePerRequestFilter { if (SecurityContextHolder.getContext().getAuthentication() == null) { try { - if (tokenService.isTokenValid(token)) { - String userId = tokenService.extractUserId(token); - String username = tokenService.extractUsername(token); - String role = tokenService.extractRole(token); + if (jwtService.isTokenValid(token)) { + String userId = jwtService.extractUserId(token); + String username = jwtService.extractUsername(token); + String role = jwtService.extractRole(token); TokenUser tokenUser = new TokenUser(userId, username, role); diff --git a/backend/src/main/java/com/serena/backend/controller/ClientController.java b/backend/src/main/java/com/serena/backend/controller/ClientController.java new file mode 100644 index 0000000..d6e9a39 --- /dev/null +++ b/backend/src/main/java/com/serena/backend/controller/ClientController.java @@ -0,0 +1,62 @@ +package com.serena.backend.controller; + +import com.serena.backend.dto.ApiResponse; +import com.serena.backend.dto.ConnectClientRequest; +import com.serena.backend.model.Client; +import com.serena.backend.service.RadioStationService; +import com.serena.backend.service.JwtService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@RestController +@RequestMapping("/api/clients") +@CrossOrigin(origins = "*") +public class ClientController { + + @Autowired + private RadioStationService radioStationService; + + @Autowired + private JwtService jwtService; + + @PostMapping("/connect") + public ResponseEntity>> connectClient(@RequestBody ConnectClientRequest request) { + // This endpoint is public - no authentication required for joining stations + Optional client; + + // If radioStationId is provided, use it with join code validation + if (request.getRadioStationId() != null && !request.getRadioStationId().isEmpty()) { + client = radioStationService.connectClient( + request.getUsername(), + request.getRadioStationId(), + request.getJoinCode()); + } else { + // Otherwise, connect by join code only + client = radioStationService.connectClientByJoinCode( + request.getUsername(), + request.getJoinCode()); + } + + if (client.isPresent()) { + // Generate a client token + String clientToken = jwtService.generateTokenWithRole(request.getUsername(), "client"); + + Map response = new HashMap<>(); + response.put("client", client.get()); + response.put("clientToken", clientToken); + response.put("message", "Successfully connected to radio station. Use this token for further requests."); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(ApiResponse.success("Successfully connected to radio station", response)); + } else { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(ApiResponse.error("Failed to connect to radio station. Invalid join code or station not found.")); + } + } +} diff --git a/backend/src/main/java/com/hackathon/backend/controller/RadioStationController.java b/backend/src/main/java/com/serena/backend/controller/RadioStationController.java similarity index 58% rename from backend/src/main/java/com/hackathon/backend/controller/RadioStationController.java rename to backend/src/main/java/com/serena/backend/controller/RadioStationController.java index c6a6e5e..b805962 100644 --- a/backend/src/main/java/com/hackathon/backend/controller/RadioStationController.java +++ b/backend/src/main/java/com/serena/backend/controller/RadioStationController.java @@ -1,12 +1,11 @@ -package com.hackathon.backend.controller; +package com.serena.backend.controller; -import com.hackathon.backend.dto.ApiResponse; -import com.hackathon.backend.dto.CreateRadioStationRequest; -import com.hackathon.backend.dto.UpdateRadioStationRequest; -import com.hackathon.backend.model.RadioStation; -import com.hackathon.backend.service.RadioStationService; -import com.hackathon.backend.service.SimpleTokenService; -import com.hackathon.backend.util.AuthUtil; +import com.serena.backend.dto.ApiResponse; +import com.serena.backend.dto.CreateRadioStationRequest; +import com.serena.backend.model.RadioStation; +import com.serena.backend.service.RadioStationService; +import com.serena.backend.service.JwtService; +import com.serena.backend.util.AuthUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -26,14 +25,15 @@ public class RadioStationController { private RadioStationService radioStationService; @Autowired - private SimpleTokenService tokenService; + private JwtService jwtService; @PostMapping - public ResponseEntity>> createRadioStation(@RequestBody CreateRadioStationRequest request) { + public ResponseEntity>> createRadioStation( + @RequestBody CreateRadioStationRequest request) { try { // Generate a token for the station owner - String ownerToken = tokenService.generateToken(request.getName() + "_owner", "owner"); - String ownerId = tokenService.extractUserId(ownerToken); + String ownerToken = jwtService.generateTokenWithRole(request.getName() + "_owner", "owner"); + String ownerId = jwtService.extractUserId(ownerToken); RadioStation station = radioStationService.createRadioStation( request.getName(), @@ -85,57 +85,6 @@ public class RadioStationController { return ResponseEntity.ok(ApiResponse.success(station.get())); } - @GetMapping("/join/{joinCode}") - public ResponseEntity> getRadioStationByJoinCode(@PathVariable String joinCode) { - // This endpoint is public - no authentication required - Optional station = radioStationService.getRadioStationByJoinCode(joinCode); - if (station.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Radio station not found with join code")); - } - - return ResponseEntity.ok(ApiResponse.success(station.get())); - } - - @PutMapping("/{stationId}") - public ResponseEntity> updateRadioStation( - @PathVariable String stationId, - @RequestBody UpdateRadioStationRequest request) { - - String currentUserId = AuthUtil.getCurrentUserId(); - if (currentUserId == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error("User not authenticated")); - } - - // Check if station exists - Optional stationOpt = radioStationService.getRadioStation(stationId); - if (stationOpt.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(ApiResponse.error("Radio station not found")); - } - - RadioStation station = stationOpt.get(); - - // Check if current user is the owner - if (!currentUserId.equals(station.getOwnerId())) { - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body(ApiResponse.error("Only the station owner can update the radio station")); - } - - Optional updated = radioStationService.updateRadioStation( - stationId, - request.getName(), - request.getDescription()); - - if (updated.isPresent()) { - return ResponseEntity.ok(ApiResponse.success("Radio station updated successfully", updated.get())); - } else { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(ApiResponse.error("Failed to update radio station")); - } - } - @DeleteMapping("/{stationId}") public ResponseEntity> deleteRadioStation(@PathVariable String stationId) { String currentUserId = AuthUtil.getCurrentUserId(); @@ -144,7 +93,6 @@ public class RadioStationController { .body(ApiResponse.error("User not authenticated")); } - // Check if station exists Optional stationOpt = radioStationService.getRadioStation(stationId); if (stationOpt.isEmpty()) { return ResponseEntity.status(HttpStatus.NOT_FOUND) @@ -153,7 +101,6 @@ public class RadioStationController { RadioStation station = stationOpt.get(); - // Check if current user is the owner if (!currentUserId.equals(station.getOwnerId())) { return ResponseEntity.status(HttpStatus.FORBIDDEN) .body(ApiResponse.error("Only the station owner can delete the radio station")); diff --git a/backend/src/main/java/com/hackathon/backend/dto/ApiResponse.java b/backend/src/main/java/com/serena/backend/dto/ApiResponse.java similarity index 96% rename from backend/src/main/java/com/hackathon/backend/dto/ApiResponse.java rename to backend/src/main/java/com/serena/backend/dto/ApiResponse.java index d58dbad..679d839 100644 --- a/backend/src/main/java/com/hackathon/backend/dto/ApiResponse.java +++ b/backend/src/main/java/com/serena/backend/dto/ApiResponse.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.dto; +package com.serena.backend.dto; public class ApiResponse { private boolean success; diff --git a/backend/src/main/java/com/hackathon/backend/dto/AuthResponse.java b/backend/src/main/java/com/serena/backend/dto/AuthResponse.java similarity index 96% rename from backend/src/main/java/com/hackathon/backend/dto/AuthResponse.java rename to backend/src/main/java/com/serena/backend/dto/AuthResponse.java index c8357be..8a3adad 100644 --- a/backend/src/main/java/com/hackathon/backend/dto/AuthResponse.java +++ b/backend/src/main/java/com/serena/backend/dto/AuthResponse.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.dto; +package com.serena.backend.dto; public class AuthResponse { private String token; diff --git a/backend/src/main/java/com/hackathon/backend/dto/ConnectClientRequest.java b/backend/src/main/java/com/serena/backend/dto/ConnectClientRequest.java similarity index 95% rename from backend/src/main/java/com/hackathon/backend/dto/ConnectClientRequest.java rename to backend/src/main/java/com/serena/backend/dto/ConnectClientRequest.java index 106f07b..8e5ad9b 100644 --- a/backend/src/main/java/com/hackathon/backend/dto/ConnectClientRequest.java +++ b/backend/src/main/java/com/serena/backend/dto/ConnectClientRequest.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.dto; +package com.serena.backend.dto; public class ConnectClientRequest { private String username; diff --git a/backend/src/main/java/com/hackathon/backend/dto/CreateRadioStationRequest.java b/backend/src/main/java/com/serena/backend/dto/CreateRadioStationRequest.java similarity index 93% rename from backend/src/main/java/com/hackathon/backend/dto/CreateRadioStationRequest.java rename to backend/src/main/java/com/serena/backend/dto/CreateRadioStationRequest.java index e7afac9..946b3fa 100644 --- a/backend/src/main/java/com/hackathon/backend/dto/CreateRadioStationRequest.java +++ b/backend/src/main/java/com/serena/backend/dto/CreateRadioStationRequest.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.dto; +package com.serena.backend.dto; public class CreateRadioStationRequest { private String name; diff --git a/backend/src/main/java/com/hackathon/backend/model/Client.java b/backend/src/main/java/com/serena/backend/model/Client.java similarity index 97% rename from backend/src/main/java/com/hackathon/backend/model/Client.java rename to backend/src/main/java/com/serena/backend/model/Client.java index 734e7d7..4427590 100644 --- a/backend/src/main/java/com/hackathon/backend/model/Client.java +++ b/backend/src/main/java/com/serena/backend/model/Client.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.model; +package com.serena.backend.model; import java.time.LocalDateTime; import java.util.UUID; diff --git a/backend/src/main/java/com/hackathon/backend/model/RadioStation.java b/backend/src/main/java/com/serena/backend/model/RadioStation.java similarity index 82% rename from backend/src/main/java/com/hackathon/backend/model/RadioStation.java rename to backend/src/main/java/com/serena/backend/model/RadioStation.java index 9cfec77..8ba3269 100644 --- a/backend/src/main/java/com/hackathon/backend/model/RadioStation.java +++ b/backend/src/main/java/com/serena/backend/model/RadioStation.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.model; +package com.serena.backend.model; import java.time.LocalDateTime; import java.util.ArrayList; @@ -15,8 +15,6 @@ public class RadioStation { private LocalDateTime createdAt; private boolean isActive; private List connectedClients; - private List songQueue; - private Song currentlyPlaying; public RadioStation() { this.id = UUID.randomUUID().toString(); @@ -24,7 +22,6 @@ public class RadioStation { this.createdAt = LocalDateTime.now(); this.isActive = true; this.connectedClients = new ArrayList<>(); - this.songQueue = new ArrayList<>(); } public RadioStation(String name, String description, String ownerId) { @@ -110,19 +107,4 @@ public class RadioStation { this.connectedClients = connectedClients; } - public List getSongQueue() { - return songQueue; - } - - public void setSongQueue(List songQueue) { - this.songQueue = songQueue; - } - - public Song getCurrentlyPlaying() { - return currentlyPlaying; - } - - public void setCurrentlyPlaying(Song currentlyPlaying) { - this.currentlyPlaying = currentlyPlaying; - } } diff --git a/backend/src/main/java/com/hackathon/backend/model/TokenUser.java b/backend/src/main/java/com/serena/backend/model/TokenUser.java similarity index 95% rename from backend/src/main/java/com/hackathon/backend/model/TokenUser.java rename to backend/src/main/java/com/serena/backend/model/TokenUser.java index 69db0a6..7e030a7 100644 --- a/backend/src/main/java/com/hackathon/backend/model/TokenUser.java +++ b/backend/src/main/java/com/serena/backend/model/TokenUser.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.model; +package com.serena.backend.model; public class TokenUser { private String userId; diff --git a/backend/src/main/java/com/hackathon/backend/service/JwtService.java b/backend/src/main/java/com/serena/backend/service/JwtService.java similarity index 69% rename from backend/src/main/java/com/hackathon/backend/service/JwtService.java rename to backend/src/main/java/com/serena/backend/service/JwtService.java index f442860..fc22dc1 100644 --- a/backend/src/main/java/com/hackathon/backend/service/JwtService.java +++ b/backend/src/main/java/com/serena/backend/service/JwtService.java @@ -1,4 +1,4 @@ -package com.hackathon.backend.service; +package com.serena.backend.service; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; @@ -32,6 +32,24 @@ public class JwtService { return createToken(claims, userId); } + // New method to generate token with role (for migrating from + // SimpleTokenService) + public String generateTokenWithRole(String username, String role) { + String userId = java.util.UUID.randomUUID().toString(); + Map claims = new HashMap<>(); + claims.put("username", username); + claims.put("role", role); // "owner" or "client" + return createToken(claims, userId); + } + + public String extractUsername(String token) { + return extractClaim(token, claims -> claims.get("username", String.class)); + } + + public String extractRole(String token) { + return extractClaim(token, claims -> claims.get("role", String.class)); + } + private String createToken(Map extraClaims, String userId) { return Jwts.builder() .claims(extraClaims) @@ -47,6 +65,16 @@ public class JwtService { return (extractedUserId.equals(userId)) && !isTokenExpired(token); } + // Overloaded method for simpler token validation (migrating from + // SimpleTokenService) + public boolean isTokenValid(String token) { + try { + return !isTokenExpired(token); + } catch (Exception e) { + return false; + } + } + private boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } diff --git a/backend/src/main/java/com/hackathon/backend/service/RadioStationService.java b/backend/src/main/java/com/serena/backend/service/RadioStationService.java similarity index 56% rename from backend/src/main/java/com/hackathon/backend/service/RadioStationService.java rename to backend/src/main/java/com/serena/backend/service/RadioStationService.java index 2284802..b81063d 100644 --- a/backend/src/main/java/com/hackathon/backend/service/RadioStationService.java +++ b/backend/src/main/java/com/serena/backend/service/RadioStationService.java @@ -1,9 +1,7 @@ -package com.hackathon.backend.service; +package com.serena.backend.service; -import com.hackathon.backend.model.RadioStation; -import com.hackathon.backend.model.Song; -import com.hackathon.backend.model.Client; -import com.hackathon.backend.model.VoteType; +import com.serena.backend.model.RadioStation; +import com.serena.backend.model.Client; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -101,8 +99,6 @@ public class RadioStationService { RadioStation station = radioStations.get(client.getRadioStationId()); if (station != null) { station.getConnectedClients().remove(clientId); - // Remove votes from all songs - station.getSongQueue().forEach(song -> song.removeVote(clientId)); } clients.remove(clientId); return true; @@ -125,83 +121,4 @@ public class RadioStationService { return new ArrayList<>(); } - // Song Management - public Optional addSongToQueue(String radioStationId, String title, String artist, - String album, int duration, String url, String addedBy) { - RadioStation station = radioStations.get(radioStationId); - if (station != null && station.isActive()) { - Song song = new Song(title, artist, album, duration, url, addedBy); - station.getSongQueue().add(song); - return Optional.of(song); - } - return Optional.empty(); - } - - public Optional voteSong(String radioStationId, String songId, String userId, VoteType voteType) { - RadioStation station = radioStations.get(radioStationId); - if (station != null) { - Optional songOpt = station.getSongQueue().stream() - .filter(song -> song.getId().equals(songId)) - .findFirst(); - - if (songOpt.isPresent()) { - Song song = songOpt.get(); - song.addVote(userId, voteType); - return Optional.of(song); - } - } - return Optional.empty(); - } - - public boolean removeSongVote(String radioStationId, String songId, String userId) { - RadioStation station = radioStations.get(radioStationId); - if (station != null) { - Optional songOpt = station.getSongQueue().stream() - .filter(song -> song.getId().equals(songId)) - .findFirst(); - - if (songOpt.isPresent()) { - Song song = songOpt.get(); - song.removeVote(userId); - return true; - } - } - return false; - } - - public List getSongQueue(String radioStationId) { - RadioStation station = radioStations.get(radioStationId); - if (station != null) { - // Sort by score (upvotes - downvotes) descending - return station.getSongQueue().stream() - .sorted((s1, s2) -> Integer.compare(s2.getScore(), s1.getScore())) - .toList(); - } - return new ArrayList<>(); - } - - public Optional getCurrentlyPlaying(String radioStationId) { - RadioStation station = radioStations.get(radioStationId); - if (station != null) { - return Optional.ofNullable(station.getCurrentlyPlaying()); - } - return Optional.empty(); - } - - public Optional playNextSong(String radioStationId) { - RadioStation station = radioStations.get(radioStationId); - if (station != null && !station.getSongQueue().isEmpty()) { - // Get the song with highest score - Song nextSong = station.getSongQueue().stream() - .max((s1, s2) -> Integer.compare(s1.getScore(), s2.getScore())) - .orElse(null); - - if (nextSong != null) { - station.getSongQueue().remove(nextSong); - station.setCurrentlyPlaying(nextSong); - return Optional.of(nextSong); - } - } - return Optional.empty(); - } } diff --git a/backend/src/main/java/com/hackathon/backend/util/AuthUtil.java b/backend/src/main/java/com/serena/backend/util/AuthUtil.java similarity index 92% rename from backend/src/main/java/com/hackathon/backend/util/AuthUtil.java rename to backend/src/main/java/com/serena/backend/util/AuthUtil.java index 0d98d07..8eb3237 100644 --- a/backend/src/main/java/com/hackathon/backend/util/AuthUtil.java +++ b/backend/src/main/java/com/serena/backend/util/AuthUtil.java @@ -1,6 +1,6 @@ -package com.hackathon.backend.util; +package com.serena.backend.util; -import com.hackathon.backend.model.TokenUser; +import com.serena.backend.model.TokenUser; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; diff --git a/backend/src/test/java/com/hackathon/backend/BackendApplicationTests.java b/backend/src/test/java/com/serena/backend/BackendApplicationTests.java similarity index 85% rename from backend/src/test/java/com/hackathon/backend/BackendApplicationTests.java rename to backend/src/test/java/com/serena/backend/BackendApplicationTests.java index 0f7bf2a..cac54fe 100644 --- a/backend/src/test/java/com/hackathon/backend/BackendApplicationTests.java +++ b/backend/src/test/java/com/serena/backend/BackendApplicationTests.java @@ -1,4 +1,4 @@ -package com.hackathon.backend; +package com.serena.backend; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/backend/src/test/java/com/hackathon/backend/service/RadioStationServiceTest.java b/backend/src/test/java/com/serena/backend/service/RadioStationServiceTest.java similarity index 97% rename from backend/src/test/java/com/hackathon/backend/service/RadioStationServiceTest.java rename to backend/src/test/java/com/serena/backend/service/RadioStationServiceTest.java index da9fe25..fe32945 100644 --- a/backend/src/test/java/com/hackathon/backend/service/RadioStationServiceTest.java +++ b/backend/src/test/java/com/serena/backend/service/RadioStationServiceTest.java @@ -1,7 +1,7 @@ -package com.hackathon.backend.service; +package com.serena.backend.service; -import com.hackathon.backend.model.RadioStation; -import com.hackathon.backend.model.Client; +import com.serena.backend.model.RadioStation; +import com.serena.backend.model.Client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName;