Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions SmartHomeCapstone/backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Gradle
.gradle/
build/
!gradle/wrapper/gradle-wrapper.jar

# IDE
.idea/
.vscode/
*.iml
*.ipr
*.iws

# OS
.DS_Store
Thumbs.db

# Git
.git/
.gitignore

# Documentation
README.md
*.md

# Logs
*.log

# Test files
src/test/

# H2 database files
*.db
*.mv.db
*.trace.db
35 changes: 35 additions & 0 deletions SmartHomeCapstone/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Use Eclipse Temurin JDK 21 as base image
FROM eclipse-temurin:21-jdk-jammy AS build

# Set working directory
WORKDIR /app

# Copy gradle wrapper and gradle files
COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .

# Make gradlew executable
RUN chmod +x gradlew

# Copy source code
COPY src src

# Build the application (skip tests for faster builds)
RUN ./gradlew clean build -x test

# Use a smaller JRE image for runtime
FROM eclipse-temurin:21-jre-jammy

# Set working directory
WORKDIR /app

# Copy the built JAR from build stage
COPY --from=build /app/build/libs/*.jar app.jar

# Expose port (Render will override this with PORT env variable)
EXPOSE 8080

# Run the application with prod profile
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]
8 changes: 7 additions & 1 deletion SmartHomeCapstone/backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'com.fasterxml.jackson.core:jackson-annotations'

// PostgreSQL for production
runtimeOnly 'org.postgresql:postgresql'

// H2 for local development (only loaded when dev profile is active)
runtimeOnly 'com.h2database:h2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
Expand All @@ -46,4 +52,4 @@ spotless {
indentWithTabs(2)
indentWithSpaces(4)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.smarthome.backend;

import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
Expand All @@ -12,6 +15,9 @@
@SpringBootApplication
public class BackendApplication {

@Value("${cors.allowed.origins:http://localhost:3000,http://127.0.0.1:3000}")
private String allowedOrigins;

public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
Expand All @@ -21,20 +27,21 @@ public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
List<String> origins = Arrays.asList(allowedOrigins.split(","));
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000", "http://127.0.0.1:3000", "http://localhost:3001")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);
.allowedOrigins(origins.toArray(new String[0]))
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);
}
};
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("http://localhost:3000");
configuration.addAllowedOrigin("http://127.0.0.1:3000");
List<String> origins = Arrays.asList(allowedOrigins.split(","));
origins.forEach(configuration::addAllowedOrigin);
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,48 @@
@Repository
public interface HomeMembershipRepository extends JpaRepository<HomeMembership, Long> {

// Find membership by user clerkId and home ID
@Query("SELECT hm FROM HomeMembership hm WHERE hm.user.clerkId = :clerkId AND hm.home.homeId = :homeId")
Optional<HomeMembership> findByClerkIdAndHomeId(@Param("clerkId") String clerkId, @Param("homeId") Long homeId);

// Find all memberships for a user
@Query("SELECT hm FROM HomeMembership hm WHERE hm.user.clerkId = :clerkId")
List<HomeMembership> findByClerkId(@Param("clerkId") String clerkId);

// Find all memberships for a home
@Query("SELECT hm FROM HomeMembership hm WHERE hm.home.homeId = :homeId")
List<HomeMembership> findByHomeId(@Param("homeId") Long homeId);

// Find memberships by role
@Query("SELECT hm FROM HomeMembership hm WHERE hm.role = :role")
List<HomeMembership> findByRole(@Param("role") MembershipRole role);

// Check if user has specific role in home
@Query("SELECT CASE WHEN COUNT(hm) > 0 THEN true ELSE false END FROM HomeMembership hm WHERE hm.user.clerkId = :clerkId AND hm.home.homeId = :homeId AND hm.role = :role")
boolean existsByClerkIdAndHomeIdAndRole(@Param("clerkId") String clerkId, @Param("homeId") Long homeId, @Param("role") MembershipRole role);

// Check if user is member of home (any role)
@Query("SELECT CASE WHEN COUNT(hm) > 0 THEN true ELSE false END FROM HomeMembership hm WHERE hm.user.clerkId = :clerkId AND hm.home.homeId = :homeId")
boolean existsByClerkIdAndHomeId(@Param("clerkId") String clerkId, @Param("homeId") Long homeId);

// Count total members in a home
@Query("SELECT COUNT(hm) FROM HomeMembership hm WHERE hm.home.homeId = :homeId")
long countByHomeId(@Param("homeId") Long homeId);

// Count admins in a home
@Query("SELECT COUNT(hm) FROM HomeMembership hm WHERE hm.home.homeId = :homeId AND hm.role = :role")
long countByHomeIdAndRole(@Param("homeId") Long homeId, @Param("role") MembershipRole role);
}
// Find membership by user clerkId and home ID
@Query(
"SELECT hm FROM HomeMembership hm WHERE hm.user.clerkId = :clerkId AND hm.home.homeId ="
+ " :homeId")
Optional<HomeMembership> findByClerkIdAndHomeId(
@Param("clerkId") String clerkId, @Param("homeId") Long homeId);

// Find all memberships for a user
@Query("SELECT hm FROM HomeMembership hm WHERE hm.user.clerkId = :clerkId")
List<HomeMembership> findByClerkId(@Param("clerkId") String clerkId);

// Find all memberships for a home
@Query("SELECT hm FROM HomeMembership hm WHERE hm.home.homeId = :homeId")
List<HomeMembership> findByHomeId(@Param("homeId") Long homeId);

// Find memberships by role
@Query("SELECT hm FROM HomeMembership hm WHERE hm.role = :role")
List<HomeMembership> findByRole(@Param("role") MembershipRole role);

// Check if user has specific role in home
@Query(
"SELECT CASE WHEN COUNT(hm) > 0 THEN true ELSE false END FROM HomeMembership hm WHERE"
+ " hm.user.clerkId = :clerkId AND hm.home.homeId = :homeId AND hm.role = :role")
boolean existsByClerkIdAndHomeIdAndRole(
@Param("clerkId") String clerkId,
@Param("homeId") Long homeId,
@Param("role") MembershipRole role);

// Check if user is member of home (any role)
@Query(
"SELECT CASE WHEN COUNT(hm) > 0 THEN true ELSE false END FROM HomeMembership hm WHERE"
+ " hm.user.clerkId = :clerkId AND hm.home.homeId = :homeId")
boolean existsByClerkIdAndHomeId(
@Param("clerkId") String clerkId, @Param("homeId") Long homeId);

// Count total members in a home
@Query("SELECT COUNT(hm) FROM HomeMembership hm WHERE hm.home.homeId = :homeId")
long countByHomeId(@Param("homeId") Long homeId);

// Count admins in a home
@Query(
"SELECT COUNT(hm) FROM HomeMembership hm WHERE hm.home.homeId = :homeId AND hm.role ="
+ " :role")
long countByHomeIdAndRole(@Param("homeId") Long homeId, @Param("role") MembershipRole role);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import com.smarthome.backend.entity.HomeMembership;
import com.smarthome.backend.entity.User;
import com.smarthome.backend.enums.MembershipRole;
import com.smarthome.backend.repository.HomeRepository;
import com.smarthome.backend.repository.HomeMembershipRepository;
import com.smarthome.backend.repository.HomeRepository;
import com.smarthome.backend.repository.UserRepository;
import java.util.List;
import java.util.Optional;
Expand All @@ -23,9 +23,10 @@ public class HomeService {
private final UserRepository userRepository;

@Autowired
public HomeService(HomeRepository homeRepository,
HomeMembershipRepository homeMembershipRepository,
UserRepository userRepository) {
public HomeService(
HomeRepository homeRepository,
HomeMembershipRepository homeMembershipRepository,
UserRepository userRepository) {
this.homeRepository = homeRepository;
this.homeMembershipRepository = homeMembershipRepository;
this.userRepository = userRepository;
Expand Down
45 changes: 45 additions & 0 deletions SmartHomeCapstone/backend/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
spring:
application:
name: backend

# PostgreSQL Database Configuration
datasource:
url: ${JDBC_DATABASE_URL}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
idle-timeout: 300000
connection-timeout: 20000

# JPA/Hibernate Configuration for PostgreSQL
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
show-sql: false
properties:
hibernate:
format_sql: false
jdbc:
lob:
non_contextual_creation: true
temp:
use_jdbc_metadata_defaults: false

# Logging (reduced for production)
logging:
level:
com.smarthome.backend: INFO
org.springframework.web: WARN
org.hibernate: WARN

# JWT Configuration (use environment variable in production)
app:
jwt:
secret: ${JWT_SECRET}
expiration: 86400000

# Server configuration
server:
port: ${PORT:8080}
41 changes: 41 additions & 0 deletions SmartHomeCapstone/backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
spring:
application:
name: backend

profiles:
active: dev

# H2 Database Configuration (for local development)
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: password

# H2 Console (for debugging/viewing data)
h2:
console:
enabled: true
path: /h2-console

# JPA/Hibernate Configuration
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true

# Logging
logging:
level:
com.smarthome.backend: DEBUG
org.springframework.web: DEBUG

# JWT Configuration
app:
jwt:
secret: myVerySecretKeyForJWTTokenGenerationThatIsLongEnoughForSecurity
expiration: 86400000