diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d1936ad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# Vibing application
+
+## Data model
+
+```mermaid
+classDiagram
+ class Activity {
+ +String name
+ +Location location
+ +Int priceRange
+ +List~String~ tags
+ }
+```
+
diff --git a/backend/pom.xml b/backend/pom.xml
index edfe43d..c2fc485 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -49,8 +49,8 @@
- com.h2database
- h2
+ org.postgresql
+ postgresql
runtime
@@ -81,12 +81,31 @@
jackson-databind
+
+
+ org.projectlombok
+ lombok
+ 1.18.38
+ provided
+
+
org.springdoc
springdoc-openapi-starter-webmvc-ui
2.2.0
+
+
+
+ org.flywaydb
+ flyway-core
+
+
+ org.flywaydb
+ flyway-database-postgresql
+ 11.10.5
+
diff --git a/backend/src/main/java/com/vibing/backend/config/SecurityConfig.java b/backend/src/main/java/com/vibing/backend/config/SecurityConfig.java
index 5e95533..bdceb7e 100644
--- a/backend/src/main/java/com/vibing/backend/config/SecurityConfig.java
+++ b/backend/src/main/java/com/vibing/backend/config/SecurityConfig.java
@@ -28,8 +28,7 @@ public class SecurityConfig {
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
+ .authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
return http.build();
}
diff --git a/backend/src/main/java/com/vibing/backend/controller/ActivityController.java b/backend/src/main/java/com/vibing/backend/controller/ActivityController.java
new file mode 100644
index 0000000..d5374d7
--- /dev/null
+++ b/backend/src/main/java/com/vibing/backend/controller/ActivityController.java
@@ -0,0 +1,117 @@
+package com.vibing.backend.controller;
+
+import com.vibing.backend.model.Activity;
+import com.vibing.backend.service.ActivityService;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * REST controller for Activity-related operations.
+ */
+@RestController
+@RequestMapping("/activities")
+@Tag(name = "Activity Management", description = "APIs for managing activities")
+@CrossOrigin(origins = "*") // Configure this properly for production
+@AllArgsConstructor
+public class ActivityController {
+
+ private final ActivityService activityService;
+
+ /**
+ * Get all activities.
+ *
+ * @return ResponseEntity containing list of all activities
+ */
+ @GetMapping
+ @Operation(summary = "Get all activities", description = "Retrieve a list of all activities")
+ public ResponseEntity> getAllActivities() {
+ List activities = activityService.findAll();
+ return ResponseEntity.ok(activities);
+ }
+
+ /**
+ * Get an activity by ID.
+ *
+ * @param id the activity ID
+ * @return ResponseEntity containing the activity if found
+ */
+ @GetMapping("/{id}")
+ @Operation(summary = "Get activity by ID", description = "Retrieve a specific activity by its ID")
+ public ResponseEntity getActivityById(@PathVariable Long id) {
+ Optional activity = activityService.findById(id);
+ return activity.map(ResponseEntity::ok)
+ .orElse(ResponseEntity.notFound().build());
+ }
+
+ /**
+ * Get activities by name (case-insensitive search).
+ *
+ * @param name the activity name to search for
+ * @return ResponseEntity containing list of matching activities
+ */
+ @GetMapping("/search/name")
+ @Operation(summary = "Search activities by name", description = "Search for activities by name (case-insensitive)")
+ public ResponseEntity> getActivitiesByName(@RequestParam String name) {
+ List activities = activityService.findByNameContainingIgnoreCase(name);
+ return ResponseEntity.ok(activities);
+ }
+
+ /**
+ * Get activities by price range.
+ *
+ * @param priceRange the price range to filter by
+ * @return ResponseEntity containing list of activities with the specified price range
+ */
+ @GetMapping("/search/price-range")
+ @Operation(summary = "Get activities by price range", description = "Retrieve activities with a specific price range")
+ public ResponseEntity> getActivitiesByPriceRange(@RequestParam Integer priceRange) {
+ List activities = activityService.findByPriceRange(priceRange);
+ return ResponseEntity.ok(activities);
+ }
+
+ /**
+ * Get activities by location.
+ *
+ * @param locationId the location ID to filter by
+ * @return ResponseEntity containing list of activities at the specified location
+ */
+ @GetMapping("/search/location")
+ @Operation(summary = "Get activities by location", description = "Retrieve activities at a specific location")
+ public ResponseEntity> getActivitiesByLocation(@RequestParam Long locationId) {
+ List activities = activityService.findByLocationId(locationId);
+ return ResponseEntity.ok(activities);
+ }
+
+ /**
+ * Get activities by tag.
+ *
+ * @param tag the tag to search for
+ * @return ResponseEntity containing list of activities with the specified tag
+ */
+ @GetMapping("/search/tag")
+ @Operation(summary = "Get activities by tag", description = "Retrieve activities that have a specific tag")
+ public ResponseEntity> getActivitiesByTag(@RequestParam String tag) {
+ List activities = activityService.findByTagsContaining(tag);
+ return ResponseEntity.ok(activities);
+ }
+
+ /**
+ * Get activities by maximum price range.
+ *
+ * @param maxPriceRange the maximum price range
+ * @return ResponseEntity containing list of activities with price range <= maxPriceRange
+ */
+ @GetMapping("/search/max-price")
+ @Operation(summary = "Get activities by maximum price range", description = "Retrieve activities with price range less than or equal to the specified value")
+ public ResponseEntity> getActivitiesByMaxPriceRange(@RequestParam Integer maxPriceRange) {
+ List activities = activityService.findByPriceRangeLessThanEqual(maxPriceRange);
+ return ResponseEntity.ok(activities);
+ }
+}
\ 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
deleted file mode 100644
index a34eded..0000000
--- a/backend/src/main/java/com/vibing/backend/controller/UserController.java
+++ /dev/null
@@ -1,142 +0,0 @@
-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/Activity.java b/backend/src/main/java/com/vibing/backend/model/Activity.java
new file mode 100644
index 0000000..3c969bf
--- /dev/null
+++ b/backend/src/main/java/com/vibing/backend/model/Activity.java
@@ -0,0 +1,67 @@
+package com.vibing.backend.model;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.Max;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * Activity entity representing an activity in the Vibing application.
+ */
+@Entity
+@Table(name = "activities")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Activity {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank(message = "Activity name is required")
+ @Column(nullable = false)
+ private String name;
+
+ @NotNull(message = "Location is required")
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "location_id", nullable = false)
+ private Location location;
+
+ @NotNull(message = "Price range is required")
+ @Min(value = 1, message = "Price range must be at least 1")
+ @Max(value = 5, message = "Price range must be at most 5")
+ @Column(name = "price_range", nullable = false)
+ private Integer priceRange;
+
+ @ElementCollection(fetch = FetchType.LAZY)
+ @CollectionTable(name = "activity_tags", joinColumns = @JoinColumn(name = "activity_id"))
+ @Column(name = "tag")
+ private List tags;
+
+ @Column(name = "description")
+ private String description;
+
+ @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();
+ }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/vibing/backend/model/Location.java b/backend/src/main/java/com/vibing/backend/model/Location.java
new file mode 100644
index 0000000..c7b2b0c
--- /dev/null
+++ b/backend/src/main/java/com/vibing/backend/model/Location.java
@@ -0,0 +1,62 @@
+package com.vibing.backend.model;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+import java.time.LocalDateTime;
+
+/**
+ * Location entity representing a location in the Vibing application.
+ */
+@Entity
+@Table(name = "locations")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Location {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank(message = "Location name is required")
+ @Column(nullable = false)
+ private String name;
+
+ @Column
+ private String address;
+
+ @Column
+ private String city;
+
+ @Column
+ private String country;
+
+ @Column(name = "postal_code")
+ private String postalCode;
+
+ @Column(name = "latitude")
+ private Double latitude;
+
+ @Column(name = "longitude")
+ private Double longitude;
+
+ @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();
+ }
+}
\ 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
deleted file mode 100644
index 1eb2329..0000000
--- a/backend/src/main/java/com/vibing/backend/model/User.java
+++ /dev/null
@@ -1,144 +0,0 @@
-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/ActivityRepository.java b/backend/src/main/java/com/vibing/backend/repository/ActivityRepository.java
new file mode 100644
index 0000000..94c6b46
--- /dev/null
+++ b/backend/src/main/java/com/vibing/backend/repository/ActivityRepository.java
@@ -0,0 +1,54 @@
+package com.vibing.backend.repository;
+
+import com.vibing.backend.model.Activity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * Repository interface for Activity entity operations.
+ */
+@Repository
+public interface ActivityRepository extends JpaRepository {
+
+ /**
+ * Find activities by name (case-insensitive).
+ *
+ * @param name the activity name to search for
+ * @return List of activities matching the name
+ */
+ List findByNameContainingIgnoreCase(String name);
+
+ /**
+ * Find activities by price range.
+ *
+ * @param priceRange the price range to search for
+ * @return List of activities with the specified price range
+ */
+ List findByPriceRange(Integer priceRange);
+
+ /**
+ * Find activities by location.
+ *
+ * @param locationId the location ID to search for
+ * @return List of activities at the specified location
+ */
+ List findByLocationId(Long locationId);
+
+ /**
+ * Find activities by tag.
+ *
+ * @param tag the tag to search for
+ * @return List of activities with the specified tag
+ */
+ List findByTagsContaining(String tag);
+
+ /**
+ * Find activities by price range less than or equal to.
+ *
+ * @param maxPriceRange the maximum price range
+ * @return List of activities with price range <= maxPriceRange
+ */
+ List findByPriceRangeLessThanEqual(Integer maxPriceRange);
+}
\ 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
deleted file mode 100644
index 34c1634..0000000
--- a/backend/src/main/java/com/vibing/backend/repository/UserRepository.java
+++ /dev/null
@@ -1,46 +0,0 @@
-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/ActivityService.java b/backend/src/main/java/com/vibing/backend/service/ActivityService.java
new file mode 100644
index 0000000..959d16c
--- /dev/null
+++ b/backend/src/main/java/com/vibing/backend/service/ActivityService.java
@@ -0,0 +1,49 @@
+package com.vibing.backend.service;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.springframework.stereotype.Service;
+
+import com.vibing.backend.model.Activity;
+import com.vibing.backend.repository.ActivityRepository;
+
+import lombok.AllArgsConstructor;
+
+/**
+ * Service class for managing activities.
+ */
+@Service
+@AllArgsConstructor
+public class ActivityService {
+
+ private final ActivityRepository activityRepository;
+
+ public List findAll() {
+ return activityRepository.findAll();
+ }
+
+ public Optional findById(Long id) {
+ return activityRepository.findById(id);
+ }
+
+ public List findByNameContainingIgnoreCase(String name) {
+ return activityRepository.findByNameContainingIgnoreCase(name);
+ }
+
+ public List findByPriceRange(Integer priceRange) {
+ return activityRepository.findByPriceRange(priceRange);
+ }
+
+ public List findByTagsContaining(String tag) {
+ return activityRepository.findByTagsContaining(tag);
+ }
+
+ public List findByLocationId(Long locationId) {
+ return activityRepository.findByLocationId(locationId);
+ }
+
+ public List findByPriceRangeLessThanEqual(Integer maxPriceRange) {
+ return activityRepository.findByPriceRangeLessThanEqual(maxPriceRange);
+ }
+}
diff --git a/backend/src/main/java/com/vibing/backend/service/UserService.java b/backend/src/main/java/com/vibing/backend/service/UserService.java
deleted file mode 100644
index 803cf12..0000000
--- a/backend/src/main/java/com/vibing/backend/service/UserService.java
+++ /dev/null
@@ -1,134 +0,0 @@
-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
index d1e21e9..f0421dd 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -9,27 +9,21 @@ spring:
# Database Configuration
datasource:
- url: jdbc:h2:mem:testdb
- driver-class-name: org.h2.Driver
- username: sa
- password: password
+ url: jdbc:postgresql://localhost:5432/vibing
+ username: vibing
+ password: vibing
+ driver-class-name: org.postgresql.Driver
# JPA Configuration
jpa:
- database-platform: org.hibernate.dialect.H2Dialect
+ database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
- ddl-auto: create-drop
+ ddl-auto: update
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
diff --git a/backend/src/main/resources/db/migration/V1__initial_schema.sql b/backend/src/main/resources/db/migration/V1__initial_schema.sql
new file mode 100644
index 0000000..5a843d9
--- /dev/null
+++ b/backend/src/main/resources/db/migration/V1__initial_schema.sql
@@ -0,0 +1,36 @@
+CREATE SCHEMA IF NOT EXISTS vibing;
+
+CREATE TABLE IF NOT EXISTS vibing.activities (
+ id SERIAL PRIMARY KEY,
+ name TEXT NOT NULL,
+ description TEXT,
+ price_range INT NOT NULL,
+ location_id INT NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT fk_location
+ FOREIGN KEY(location_id)
+ REFERENCES vibing.locations(id)
+ ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS vibing.activity_tags (
+ activity_id INT NOT NULL,
+ tag TEXT NOT NULL,
+ FOREIGN KEY (activity_id) REFERENCES vibing.activities(id) ON DELETE CASCADE,
+ PRIMARY KEY (activity_id, tag)
+);
+
+CREATE TABLE IF NOT EXISTS vibing.locations (
+ id SERIAL PRIMARY KEY,
+ name TEXT NOT NULL,
+ address TEXT,
+ city TEXT,
+ country TEXT,
+ postal_code TEXT,
+ latitude DOUBLE PRECISION,
+ longitude DOUBLE PRECISION,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
diff --git a/backend/src/test/resources/application-test.yml b/backend/src/test/resources/application-test.yml
new file mode 100644
index 0000000..8d92abd
--- /dev/null
+++ b/backend/src/test/resources/application-test.yml
@@ -0,0 +1,10 @@
+spring:
+ datasource:
+ url: jdbc:postgresql://localhost:5432/your_test_database
+ username: test_user
+ password: test_password
+
+ flyway:
+ enabled: true
+ clean-disabled: false # Allow clean in tests
+ locations: classpath:db/migration
\ No newline at end of file
diff --git a/local-dev/compose.yml b/local-dev/compose.yml
new file mode 100644
index 0000000..a1fc9b5
--- /dev/null
+++ b/local-dev/compose.yml
@@ -0,0 +1,11 @@
+services:
+ postgres:
+ image: postgres:15
+ ports:
+ - 5432:5432
+ environment:
+ POSTGRES_USER: vibing
+ POSTGRES_PASSWORD: vibing
+ POSTGRES_DB: vibing
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
\ No newline at end of file