diff --git a/.gitea/README.md b/.gitea/README.md new file mode 100644 index 0000000..8304722 --- /dev/null +++ b/.gitea/README.md @@ -0,0 +1,49 @@ +# Gitea Actions for Vibing Project + +This directory contains Gitea Actions workflows for the Vibing project (both frontend and backend). + +## Workflows + +### Backend CI (`backend-ci.yml`) + +Runs Maven tests on the backend project. + +**Triggers:** +- Push to `main` or `develop` branches +- Pull requests to `main` or `develop` branches +- Only when changes are made to files in the `backend/` directory + +**What it does:** +1. Checks out the code +2. Sets up Java 21 (Temurin distribution) +3. Caches Maven dependencies for faster builds +4. Runs `mvn test` in the backend directory +5. Uploads test results as artifacts (retained for 7 days) + +**Requirements:** +- Java 21 +- Maven +- Ubuntu runner + +### Frontend CI (`frontend-ci.yml`) + +Runs linting and builds the frontend project. + +**Triggers:** +- Push to `main` or `develop` branches +- Pull requests to `main` or `develop` branches +- Only when changes are made to files in the `frontend/` directory + +**What it does:** +1. Checks out the code +2. Sets up Node.js 18 +3. Caches npm dependencies for faster builds +4. Installs dependencies with `npm ci` +5. Runs ESLint for code quality checks +6. Builds the project with `npm run build` +7. Uploads build artifacts (retained for 7 days) + +**Requirements:** +- Node.js 18 +- npm +- Ubuntu runner diff --git a/.gitea/workflows/backend-ci.yml b/.gitea/workflows/backend-ci.yml new file mode 100644 index 0000000..e1136bc --- /dev/null +++ b/.gitea/workflows/backend-ci.yml @@ -0,0 +1,44 @@ +name: Backend CI + +on: + push: + branches: [ main, develop ] + paths: + - 'backend/**' + pull_request: + branches: [ main, develop ] + paths: + - 'backend/**' + +jobs: + test: + name: Run Maven Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Set up Maven + uses: stCarolas/setup-maven@v5 + with: + maven-version: '3.6.3' + + - name: Run Maven tests + working-directory: ./backend + run: mvn test + + - name: Upload test results + uses: actions/upload-artifact@v3 + if: always() + with: + name: backend-test-results + path: backend/target/surefire-reports/ + retention-days: 7 \ No newline at end of file diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..9cbbae6 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,99 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +# IDE files +.idea/ +*.iws +*.iml +*.ipr +.vscode/ +.settings/ +.project +.classpath + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Application specific +application-local.yml +application-prod.yml +*.db +*.sqlite + +# Logs +logs/ +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ + +# nyc test coverage +.nyc_output + +# Dependency directories +node_modules/ + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.local +.env.development.local +.env.test.local +.env.production.local \ No newline at end of file diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..25a9f79 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,193 @@ +# Vibing Backend + +A REST API backend service built with Spring Boot, Java 21, and Maven for the Vibing application. + +## Features + +- **RESTful API**: Complete CRUD operations for user management +- **Spring Boot 3.2.0**: Latest Spring Boot version with Java 21 support +- **JPA/Hibernate**: Database persistence with H2 in-memory database +- **Spring Security**: Basic security configuration (extensible for production) +- **API Documentation**: Swagger/OpenAPI 3 documentation +- **Validation**: Input validation using Bean Validation +- **Testing**: JUnit 5 test framework + +## Prerequisites + +- Java 21 or higher +- Maven 3.6 or higher +- IDE (IntelliJ IDEA, Eclipse, VS Code, etc.) + +## Getting Started + +### 1. Clone the Repository + +```bash +git clone +cd vibing/backend +``` + +### 2. Build the Project + +```bash +mvn clean install +``` + +### 3. Run the Application + +```bash +mvn spring-boot:run +``` + +The application will start on `http://localhost:8080` + +## API Endpoints + +### Base URL +- **API Base**: `http://localhost:8080/api` +- **Health Check**: `http://localhost:8080/api/health` +- **Swagger UI**: `http://localhost:8080/swagger-ui.html` +- **H2 Console**: `http://localhost:8080/h2-console` + +### User Management Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/users` | Get all users | +| GET | `/api/users/{id}` | Get user by ID | +| GET | `/api/users/username/{username}` | Get user by username | +| POST | `/api/users` | Create new user | +| PUT | `/api/users/{id}` | Update user | +| DELETE | `/api/users/{id}` | Delete user | +| GET | `/api/users/check-username/{username}` | Check username availability | +| GET | `/api/users/check-email/{email}` | Check email availability | + +### Health Check Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/health` | Application health status | +| GET | `/api/health/ping` | Simple ping endpoint | + +## Database + +The application uses H2 in-memory database for development: + +- **URL**: `jdbc:h2:mem:testdb` +- **Username**: `sa` +- **Password**: `password` +- **Console**: `http://localhost:8080/h2-console` + +## Project Structure + +``` +src/ +├── main/ +│ ├── java/com/vibing/backend/ +│ │ ├── VibingBackendApplication.java # Main application class +│ │ ├── controller/ # REST controllers +│ │ │ ├── UserController.java +│ │ │ └── HealthController.java +│ │ ├── service/ # Business logic +│ │ │ └── UserService.java +│ │ ├── repository/ # Data access layer +│ │ │ └── UserRepository.java +│ │ ├── model/ # Entity classes +│ │ │ └── User.java +│ │ ├── config/ # Configuration classes +│ │ │ └── SecurityConfig.java +│ │ └── exception/ # Custom exceptions +│ └── resources/ +│ └── application.yml # Application configuration +└── test/ + └── java/com/vibing/backend/ + └── VibingBackendApplicationTests.java +``` + +## Configuration + +The application configuration is in `src/main/resources/application.yml`: + +- **Server Port**: 8080 +- **Context Path**: `/api` +- **Database**: H2 in-memory +- **JPA**: Hibernate with create-drop DDL +- **Security**: Basic configuration (allows all user endpoints) + +## Development + +### Running Tests + +```bash +mvn test +``` + +### Building JAR + +```bash +mvn clean package +``` + +The JAR file will be created in the `target/` directory. + +### Running JAR + +```bash +java -jar target/vibing-backend-1.0.0.jar +``` + +## API Documentation + +Once the application is running, you can access: + +- **Swagger UI**: `http://localhost:8080/swagger-ui.html` +- **OpenAPI JSON**: `http://localhost:8080/api-docs` + +## Example API Usage + +### Create a User + +```bash +curl -X POST http://localhost:8080/api/users \ + -H "Content-Type: application/json" \ + -d '{ + "username": "john_doe", + "email": "john@example.com", + "password": "password123", + "firstName": "John", + "lastName": "Doe" + }' +``` + +### Get All Users + +```bash +curl http://localhost:8080/api/users +``` + +### Get User by ID + +```bash +curl http://localhost:8080/api/users/1 +``` + +## Security + +The current security configuration allows all requests to user endpoints for development purposes. For production: + +1. Implement proper authentication (JWT, OAuth2, etc.) +2. Add role-based authorization +3. Configure CORS properly +4. Use HTTPS +5. Implement rate limiting + +## Contributing + +1. Create a feature branch +2. Make your changes +3. Add tests +4. Submit a pull request + +## License + +This project is licensed under the MIT License. \ No newline at end of file diff --git a/backend/pom.xml b/backend/pom.xml new file mode 100644 index 0000000..edfe43d --- /dev/null +++ b/backend/pom.xml @@ -0,0 +1,116 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.0 + + + + com.vibing + vibing-backend + 1.0.0 + Vibing Backend + REST API backend service for Vibing application + + + 21 + 21 + 21 + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-security + + + + + com.h2database + h2 + runtime + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.security + spring-security-test + test + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.2.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + + \ No newline at end of file diff --git a/backend/run.sh b/backend/run.sh new file mode 100755 index 0000000..ef49611 --- /dev/null +++ b/backend/run.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Vibing Backend Run Script +echo "Starting Vibing Backend..." + +# Check if Java 21 is available +if ! command -v java &> /dev/null; then + echo "Error: Java is not installed or not in PATH" + exit 1 +fi + +# Check Java version +JAVA_VERSION=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2 | cut -d'.' -f1) +if [ "$JAVA_VERSION" != "21" ]; then + echo "Warning: Java 21 is recommended. Current version: $JAVA_VERSION" +fi + +# Check if Maven is available +if ! command -v mvn &> /dev/null; then + echo "Error: Maven is not installed or not in PATH" + exit 1 +fi + +echo "Building and running the application..." +echo "The application will be available at:" +echo " - API Base: http://localhost:8080/api" +echo " - Health Check: http://localhost:8080/api/health" +echo " - Swagger UI: http://localhost:8080/swagger-ui.html" +echo " - H2 Console: http://localhost:8080/h2-console" +echo "" +echo "Press Ctrl+C to stop the application" +echo "" + +# Run the application +mvn spring-boot:run \ No newline at end of file diff --git a/backend/src/main/java/com/vibing/backend/VibingBackendApplication.java b/backend/src/main/java/com/vibing/backend/VibingBackendApplication.java new file mode 100644 index 0000000..e8a45ae --- /dev/null +++ b/backend/src/main/java/com/vibing/backend/VibingBackendApplication.java @@ -0,0 +1,16 @@ +package com.vibing.backend; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Main Spring Boot application class for the Vibing backend service. + * This class serves as the entry point for the REST API backend. + */ +@SpringBootApplication +public class VibingBackendApplication { + + public static void main(String[] args) { + SpringApplication.run(VibingBackendApplication.class, args); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/vibing/backend/config/SecurityConfig.java b/backend/src/main/java/com/vibing/backend/config/SecurityConfig.java new file mode 100644 index 0000000..5e95533 --- /dev/null +++ b/backend/src/main/java/com/vibing/backend/config/SecurityConfig.java @@ -0,0 +1,36 @@ +package com.vibing.backend.config; + +import org.springframework.context.annotation.Bean; +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.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; + +/** + * Security configuration for the Vibing backend application. + * This is a basic configuration that can be extended for production use. + */ +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + /** + * Configure security filter chain. + * For development purposes, this allows all requests. + * In production, you should implement proper authentication and authorization. + * + * @param http the HttpSecurity object + * @return SecurityFilterChain + * @throws Exception if configuration fails + */ + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) // Disable CSRF for REST APIa + .authorizeHttpRequests(authz -> authz.anyRequest().permitAll()) + .headers(headers -> headers.frameOptions().disable()); // Allow H2 console frames + + return http.build(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/vibing/backend/controller/HealthController.java b/backend/src/main/java/com/vibing/backend/controller/HealthController.java new file mode 100644 index 0000000..08cd3fb --- /dev/null +++ b/backend/src/main/java/com/vibing/backend/controller/HealthController.java @@ -0,0 +1,49 @@ +package com.vibing.backend.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +/** + * Health check controller for monitoring the application status. + */ +@RestController +@RequestMapping("/health") +@Tag(name = "Health Check", description = "APIs for checking application health") +public class HealthController { + + /** + * Get the health status of the application. + * + * @return ResponseEntity containing health information + */ + @GetMapping + @Operation(summary = "Health check", description = "Check if the application is running") + public ResponseEntity> healthCheck() { + Map healthInfo = new HashMap<>(); + healthInfo.put("status", "UP"); + healthInfo.put("timestamp", LocalDateTime.now()); + healthInfo.put("service", "Vibing Backend"); + healthInfo.put("version", "1.0.0"); + + return ResponseEntity.ok(healthInfo); + } + + /** + * Get a simple ping response. + * + * @return ResponseEntity with "pong" message + */ + @GetMapping("/ping") + @Operation(summary = "Ping", description = "Simple ping endpoint") + public ResponseEntity ping() { + return ResponseEntity.ok("pong"); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/vibing/backend/controller/UserController.java b/backend/src/main/java/com/vibing/backend/controller/UserController.java new file mode 100644 index 0000000..a34eded --- /dev/null +++ b/backend/src/main/java/com/vibing/backend/controller/UserController.java @@ -0,0 +1,142 @@ +package com.vibing.backend.controller; + +import com.vibing.backend.model.User; +import com.vibing.backend.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +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; + +/** + * REST controller for User-related operations. + */ +@RestController +@RequestMapping("/users") +@Tag(name = "User Management", description = "APIs for managing users") +@CrossOrigin(origins = "*") // Configure this properly for production +public class UserController { + + private final UserService userService; + + @Autowired + public UserController(UserService userService) { + this.userService = userService; + } + + /** + * Get all users. + * + * @return ResponseEntity containing list of users + */ + @GetMapping + @Operation(summary = "Get all users", description = "Retrieve a list of all users") + public ResponseEntity> getAllUsers() { + List users = userService.getAllUsers(); + return ResponseEntity.ok(users); + } + + /** + * Get a user by ID. + * + * @param id the user ID + * @return ResponseEntity containing the user if found + */ + @GetMapping("/{id}") + @Operation(summary = "Get user by ID", description = "Retrieve a specific user by their ID") + public ResponseEntity getUserById(@PathVariable Long id) { + Optional user = userService.getUserById(id); + return user.map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * Get a user by username. + * + * @param username the username + * @return ResponseEntity containing the user if found + */ + @GetMapping("/username/{username}") + @Operation(summary = "Get user by username", description = "Retrieve a specific user by their username") + public ResponseEntity getUserByUsername(@PathVariable String username) { + Optional user = userService.getUserByUsername(username); + return user.map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * Create a new user. + * + * @param user the user to create + * @return ResponseEntity containing the created user + */ + @PostMapping + @Operation(summary = "Create a new user", description = "Create a new user account") + public ResponseEntity createUser(@Valid @RequestBody User user) { + try { + User createdUser = userService.createUser(user); + return ResponseEntity.status(HttpStatus.CREATED).body(createdUser); + } catch (RuntimeException e) { + return ResponseEntity.badRequest().build(); + } + } + + /** + * Update an existing user. + * + * @param id the user ID + * @param user the updated user data + * @return ResponseEntity containing the updated user + */ + @PutMapping("/{id}") + @Operation(summary = "Update user", description = "Update an existing user's information") + public ResponseEntity updateUser(@PathVariable Long id, @Valid @RequestBody User user) { + Optional updatedUser = userService.updateUser(id, user); + return updatedUser.map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * Delete a user by ID. + * + * @param id the user ID + * @return ResponseEntity indicating success or failure + */ + @DeleteMapping("/{id}") + @Operation(summary = "Delete user", description = "Delete a user by their ID") + public ResponseEntity deleteUser(@PathVariable Long id) { + boolean deleted = userService.deleteUser(id); + return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build(); + } + + /** + * Check if a username exists. + * + * @param username the username to check + * @return ResponseEntity with boolean indicating existence + */ + @GetMapping("/check-username/{username}") + @Operation(summary = "Check username availability", description = "Check if a username is already taken") + public ResponseEntity checkUsernameExists(@PathVariable String username) { + boolean exists = userService.userExistsByUsername(username); + return ResponseEntity.ok(exists); + } + + /** + * Check if an email exists. + * + * @param email the email to check + * @return ResponseEntity with boolean indicating existence + */ + @GetMapping("/check-email/{email}") + @Operation(summary = "Check email availability", description = "Check if an email is already registered") + public ResponseEntity checkEmailExists(@PathVariable String email) { + boolean exists = userService.userExistsByEmail(email); + return ResponseEntity.ok(exists); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/vibing/backend/model/User.java b/backend/src/main/java/com/vibing/backend/model/User.java new file mode 100644 index 0000000..1eb2329 --- /dev/null +++ b/backend/src/main/java/com/vibing/backend/model/User.java @@ -0,0 +1,144 @@ +package com.vibing.backend.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import java.time.LocalDateTime; + +/** + * User entity representing a user in the Vibing application. + */ +@Entity +@Table(name = "users") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank(message = "Username is required") + @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters") + @Column(unique = true, nullable = false) + private String username; + + @NotBlank(message = "Email is required") + @Email(message = "Email should be valid") + @Column(unique = true, nullable = false) + private String email; + + @NotBlank(message = "Password is required") + @Size(min = 6, message = "Password must be at least 6 characters") + @Column(nullable = false) + private String password; + + @Column(name = "first_name") + private String firstName; + + @Column(name = "last_name") + private String lastName; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + @PrePersist + protected void onCreate() { + createdAt = LocalDateTime.now(); + updatedAt = LocalDateTime.now(); + } + + @PreUpdate + protected void onUpdate() { + updatedAt = LocalDateTime.now(); + } + + // Constructors + public User() {} + + public User(String username, String email, String password) { + this.username = username; + this.email = email; + this.password = password; + } + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", username='" + username + '\'' + + ", email='" + email + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", createdAt=" + createdAt + + ", updatedAt=" + updatedAt + + '}'; + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/vibing/backend/repository/UserRepository.java b/backend/src/main/java/com/vibing/backend/repository/UserRepository.java new file mode 100644 index 0000000..34c1634 --- /dev/null +++ b/backend/src/main/java/com/vibing/backend/repository/UserRepository.java @@ -0,0 +1,46 @@ +package com.vibing.backend.repository; + +import com.vibing.backend.model.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/** + * Repository interface for User entity operations. + */ +@Repository +public interface UserRepository extends JpaRepository { + + /** + * Find a user by username. + * + * @param username the username to search for + * @return Optional containing the user if found + */ + Optional findByUsername(String username); + + /** + * Find a user by email. + * + * @param email the email to search for + * @return Optional containing the user if found + */ + Optional findByEmail(String email); + + /** + * Check if a user exists by username. + * + * @param username the username to check + * @return true if user exists, false otherwise + */ + boolean existsByUsername(String username); + + /** + * Check if a user exists by email. + * + * @param email the email to check + * @return true if user exists, false otherwise + */ + boolean existsByEmail(String email); +} \ No newline at end of file diff --git a/backend/src/main/java/com/vibing/backend/service/UserService.java b/backend/src/main/java/com/vibing/backend/service/UserService.java new file mode 100644 index 0000000..803cf12 --- /dev/null +++ b/backend/src/main/java/com/vibing/backend/service/UserService.java @@ -0,0 +1,134 @@ +package com.vibing.backend.service; + +import com.vibing.backend.model.User; +import com.vibing.backend.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +/** + * Service class for User-related business operations. + */ +@Service +public class UserService { + + private final UserRepository userRepository; + + @Autowired + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + /** + * Get all users. + * + * @return List of all users + */ + public List getAllUsers() { + return userRepository.findAll(); + } + + /** + * Get a user by ID. + * + * @param id the user ID + * @return Optional containing the user if found + */ + public Optional getUserById(Long id) { + return userRepository.findById(id); + } + + /** + * Get a user by username. + * + * @param username the username + * @return Optional containing the user if found + */ + public Optional getUserByUsername(String username) { + return userRepository.findByUsername(username); + } + + /** + * Get a user by email. + * + * @param email the email + * @return Optional containing the user if found + */ + public Optional getUserByEmail(String email) { + return userRepository.findByEmail(email); + } + + /** + * Create a new user. + * + * @param user the user to create + * @return the created user + */ + public User createUser(User user) { + // Check if username already exists + if (userRepository.existsByUsername(user.getUsername())) { + throw new RuntimeException("Username already exists: " + user.getUsername()); + } + + // Check if email already exists + if (userRepository.existsByEmail(user.getEmail())) { + throw new RuntimeException("Email already exists: " + user.getEmail()); + } + + return userRepository.save(user); + } + + /** + * Update an existing user. + * + * @param id the user ID + * @param user the updated user data + * @return Optional containing the updated user if found + */ + public Optional updateUser(Long id, User user) { + return userRepository.findById(id) + .map(existingUser -> { + existingUser.setUsername(user.getUsername()); + existingUser.setEmail(user.getEmail()); + existingUser.setFirstName(user.getFirstName()); + existingUser.setLastName(user.getLastName()); + return userRepository.save(existingUser); + }); + } + + /** + * Delete a user by ID. + * + * @param id the user ID + * @return true if user was deleted, false if not found + */ + public boolean deleteUser(Long id) { + if (userRepository.existsById(id)) { + userRepository.deleteById(id); + return true; + } + return false; + } + + /** + * Check if a user exists by username. + * + * @param username the username to check + * @return true if user exists, false otherwise + */ + public boolean userExistsByUsername(String username) { + return userRepository.existsByUsername(username); + } + + /** + * Check if a user exists by email. + * + * @param email the email to check + * @return true if user exists, false otherwise + */ + public boolean userExistsByEmail(String email) { + return userRepository.existsByEmail(email); + } +} \ No newline at end of file diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml new file mode 100644 index 0000000..d1e21e9 --- /dev/null +++ b/backend/src/main/resources/application.yml @@ -0,0 +1,62 @@ +server: + port: 8080 + servlet: + context-path: /api + +spring: + application: + name: vibing-backend + + # Database Configuration + datasource: + url: jdbc:h2:mem:testdb + driver-class-name: org.h2.Driver + username: sa + password: password + + # JPA Configuration + jpa: + database-platform: org.hibernate.dialect.H2Dialect + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + format_sql: true + + # H2 Console (for development) + h2: + console: + enabled: true + path: /h2-console + + # Jackson Configuration + jackson: + default-property-inclusion: non_null + serialization: + write-dates-as-timestamps: false + deserialization: + fail-on-unknown-properties: false + +# Logging Configuration +logging: + level: + com.vibing.backend: DEBUG + org.springframework.security: DEBUG + org.springframework.web: DEBUG + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n" + +# API Documentation +springdoc: + api-docs: + path: /api-docs + swagger-ui: + path: /swagger-ui.html + operations-sorter: method + +# Security Configuration +security: + jwt: + secret: your-secret-key-here-change-in-production + expiration: 86400000 # 24 hours in milliseconds \ No newline at end of file diff --git a/backend/src/test/java/com/vibing/backend/VibingBackendApplicationTests.java b/backend/src/test/java/com/vibing/backend/VibingBackendApplicationTests.java new file mode 100644 index 0000000..02d8a52 --- /dev/null +++ b/backend/src/test/java/com/vibing/backend/VibingBackendApplicationTests.java @@ -0,0 +1,16 @@ +package com.vibing.backend; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * Basic test class for the Vibing Backend application. + */ +@SpringBootTest +class VibingBackendApplicationTests { + + @Test + void contextLoads() { + // This test verifies that the Spring application context loads successfully + } +} \ No newline at end of file