Compare commits
6 Commits
activity-t
...
2e14ea06c9
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e14ea06c9 | |||
| 2a0d7fa1b8 | |||
| 823f91804a | |||
| c5433dfefd | |||
| 8af5ea02cd | |||
| f1dff9684b |
49
.gitea/README.md
Normal file
49
.gitea/README.md
Normal file
@@ -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
|
||||
44
.gitea/workflows/backend-ci.yml
Normal file
44
.gitea/workflows/backend-ci.yml
Normal file
@@ -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
|
||||
14
README.md
Normal file
14
README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Vibing application
|
||||
|
||||
## Data model
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Activity {
|
||||
+String name
|
||||
+Location location
|
||||
+Int priceRange
|
||||
+List~String~ tags
|
||||
}
|
||||
```
|
||||
|
||||
99
backend/.gitignore
vendored
Normal file
99
backend/.gitignore
vendored
Normal file
@@ -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
|
||||
193
backend/README.md
Normal file
193
backend/README.md
Normal file
@@ -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 <repository-url>
|
||||
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.
|
||||
135
backend/pom.xml
Normal file
135
backend/pom.xml
Normal file
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>com.vibing</groupId>
|
||||
<artifactId>vibing-backend</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>Vibing Backend</name>
|
||||
<description>REST API backend service for Vibing application</description>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Starters -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Development Tools -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON Processing -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.38</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- API Documentation -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Flyway -->
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-database-postgresql</artifactId>
|
||||
<version>11.10.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
35
backend/run.sh
Executable file
35
backend/run.sh
Executable file
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@@ -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<List<Activity>> getAllActivities() {
|
||||
List<Activity> 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<Activity> getActivityById(@PathVariable Long id) {
|
||||
Optional<Activity> 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<List<Activity>> getActivitiesByName(@RequestParam String name) {
|
||||
List<Activity> 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<List<Activity>> getActivitiesByPriceRange(@RequestParam Integer priceRange) {
|
||||
List<Activity> 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<List<Activity>> getActivitiesByLocation(@RequestParam Long locationId) {
|
||||
List<Activity> 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<List<Activity>> getActivitiesByTag(@RequestParam String tag) {
|
||||
List<Activity> 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<List<Activity>> getActivitiesByMaxPriceRange(@RequestParam Integer maxPriceRange) {
|
||||
List<Activity> activities = activityService.findByPriceRangeLessThanEqual(maxPriceRange);
|
||||
return ResponseEntity.ok(activities);
|
||||
}
|
||||
}
|
||||
@@ -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<Map<String, Object>> healthCheck() {
|
||||
Map<String, Object> 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<String> ping() {
|
||||
return ResponseEntity.ok("pong");
|
||||
}
|
||||
}
|
||||
67
backend/src/main/java/com/vibing/backend/model/Activity.java
Normal file
67
backend/src/main/java/com/vibing/backend/model/Activity.java
Normal file
@@ -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<String> 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();
|
||||
}
|
||||
}
|
||||
62
backend/src/main/java/com/vibing/backend/model/Location.java
Normal file
62
backend/src/main/java/com/vibing/backend/model/Location.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<Activity, Long> {
|
||||
|
||||
/**
|
||||
* Find activities by name (case-insensitive).
|
||||
*
|
||||
* @param name the activity name to search for
|
||||
* @return List of activities matching the name
|
||||
*/
|
||||
List<Activity> 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<Activity> findByPriceRange(Integer priceRange);
|
||||
|
||||
/**
|
||||
* Find activities by location.
|
||||
*
|
||||
* @param locationId the location ID to search for
|
||||
* @return List of activities at the specified location
|
||||
*/
|
||||
List<Activity> findByLocationId(Long locationId);
|
||||
|
||||
/**
|
||||
* Find activities by tag.
|
||||
*
|
||||
* @param tag the tag to search for
|
||||
* @return List of activities with the specified tag
|
||||
*/
|
||||
List<Activity> 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<Activity> findByPriceRangeLessThanEqual(Integer maxPriceRange);
|
||||
}
|
||||
@@ -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<Activity> findAll() {
|
||||
return activityRepository.findAll();
|
||||
}
|
||||
|
||||
public Optional<Activity> findById(Long id) {
|
||||
return activityRepository.findById(id);
|
||||
}
|
||||
|
||||
public List<Activity> findByNameContainingIgnoreCase(String name) {
|
||||
return activityRepository.findByNameContainingIgnoreCase(name);
|
||||
}
|
||||
|
||||
public List<Activity> findByPriceRange(Integer priceRange) {
|
||||
return activityRepository.findByPriceRange(priceRange);
|
||||
}
|
||||
|
||||
public List<Activity> findByTagsContaining(String tag) {
|
||||
return activityRepository.findByTagsContaining(tag);
|
||||
}
|
||||
|
||||
public List<Activity> findByLocationId(Long locationId) {
|
||||
return activityRepository.findByLocationId(locationId);
|
||||
}
|
||||
|
||||
public List<Activity> findByPriceRangeLessThanEqual(Integer maxPriceRange) {
|
||||
return activityRepository.findByPriceRangeLessThanEqual(maxPriceRange);
|
||||
}
|
||||
}
|
||||
56
backend/src/main/resources/application.yml
Normal file
56
backend/src/main/resources/application.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /api
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: vibing-backend
|
||||
|
||||
# Database Configuration
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:5432/vibing
|
||||
username: vibing
|
||||
password: vibing
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
# JPA Configuration
|
||||
jpa:
|
||||
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
|
||||
# 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
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
10
backend/src/test/resources/application-test.yml
Normal file
10
backend/src/test/resources/application-test.yml
Normal file
@@ -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
|
||||
11
local-dev/compose.yml
Normal file
11
local-dev/compose.yml
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user