diff --git a/.gitignore b/.gitignore index b7bcada..aca8222 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ /target/ /.idea/ /.jpb/ -/application.properties +/*.db \ No newline at end of file diff --git a/application.properties b/application.properties new file mode 100644 index 0000000..af357e3 --- /dev/null +++ b/application.properties @@ -0,0 +1,10 @@ +#logging.level.de.ph87.homeautomation=DEBUG +#- +spring.datasource.url=jdbc:h2:./Homeautomation;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +#- +spring.jpa.hibernate.ddl-auto=update +de.ph87.homeautomation.insert-demo-data=true \ No newline at end of file diff --git a/deploy.sh b/deploy.sh deleted file mode 100755 index 198041f..0000000 --- a/deploy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -cd "$(dirname "$0")" || exit 1 - -mvn clean package spring-boot:repackage && \ -scp target/Homeautomation.jar media@10.0.0.50:/home/media/java/Homeautomation/Homeautomation.jar.update && \ -curl -m 2 -s http://10.0.0.50:8080/server/shutdown && echo "Server restarting..." || echo "Failed to restart server!" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 50c1f6c..d5a6b84 100644 --- a/pom.xml +++ b/pom.xml @@ -7,16 +7,17 @@ de.ph87 Homeautomation 1.0-SNAPSHOT + war - 15 - 15 + 11 + 11 org.springframework.boot spring-boot-starter-parent - 2.3.6.RELEASE + 2.6.4 @@ -28,10 +29,6 @@ org.springframework.boot spring-boot-starter-data-jpa - - org.springframework.boot - spring-boot-starter-security - org.springframework.boot spring-boot-configuration-processor @@ -40,15 +37,24 @@ org.springframework.boot spring-boot-starter-websocket + + org.springframework.boot + spring-boot-starter-tomcat + provided + com.h2database h2 + + org.postgresql + postgresql + + org.projectlombok lombok - 1.18.22 @@ -148,18 +154,6 @@ - - org.springframework.boot - spring-boot-maven-plugin - - - - de.ph87.homeautomation.BackendApplication - - - - - diff --git a/src/main/angular/.gitignore b/src/main/angular/.gitignore index 62a055a..812ec09 100644 --- a/src/main/angular/.gitignore +++ b/src/main/angular/.gitignore @@ -1,5 +1,8 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. -/node/ + +### PATRICK +/node +### END # compiled output /dist diff --git a/src/main/angular/package.json b/src/main/angular/package.json index bc19afb..d10ccfd 100644 --- a/src/main/angular/package.json +++ b/src/main/angular/package.json @@ -4,7 +4,7 @@ "scripts": { "ng": "ng", "start": "ng serve", - "build": "ng build", + "build": "ng build --base-href /Homeautomation/", "watch": "ng build --watch --configuration development", "test": "ng test" }, diff --git a/src/main/angular/src/app/api/api.service.ts b/src/main/angular/src/app/api/api.service.ts index 51fb3f9..beaa1ac 100644 --- a/src/main/angular/src/app/api/api.service.ts +++ b/src/main/angular/src/app/api/api.service.ts @@ -5,6 +5,7 @@ import {environment} from "../../environments/environment"; import {Subject} from "rxjs"; import {CompatClient, Stomp} from "@stomp/stompjs"; import {Update} from "./Update"; +import {LocationStrategy} from "@angular/common"; export function NO_OP() { } @@ -21,7 +22,7 @@ function errorInterceptor(errorHandler: (error: any) => void): ((error: any) => } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ApiService { @@ -30,10 +31,12 @@ export class ApiService { private updateSubject = new Subject>(); constructor( - private http: HttpClient, + protected readonly http: HttpClient, + protected readonly locationStrategy: LocationStrategy, ) { + const url = this.websocketUrl("websocket"); this.webSocketClient = Stomp.over(function () { - return new WebSocket(ApiService.url("ws", "websocket")); + return new WebSocket(url); }); this.webSocketClient.debug = () => null; this.webSocketClient.connect({}, () => { @@ -46,27 +49,31 @@ export class ApiService { } getItem(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) { - this.http.get(ApiService.url("http", path)).pipe(map(fromJson)).subscribe(next, errorInterceptor(error)); + this.http.get(this.restUrl(path)).pipe(map(fromJson)).subscribe(next, errorInterceptor(error)); } getList(path: string, fromJson: (json: any) => T, compare: (a: T, b: T) => number = NO_COMPARE, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) { - this.http.get(ApiService.url("http", path)).pipe(map(list => list.map(fromJson).sort(compare))).subscribe(next, errorInterceptor(error)); + this.http.get(this.restUrl(path)).pipe(map(list => list.map(fromJson).sort(compare))).subscribe(next, errorInterceptor(error)); } postReturnNone(path: string, data: any, next: (_: void) => void = NO_OP, error: (error: any) => void = NO_OP) { - this.http.post(ApiService.url("http", path), data).subscribe(next, errorInterceptor(error)); + this.http.post(this.restUrl(path), data).subscribe(next, errorInterceptor(error)); } postReturnItem(path: string, data: any, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) { - this.http.post(ApiService.url("http", path), data).pipe(map(fromJson)).subscribe(next, errorInterceptor(error)); + this.http.post(this.restUrl(path), data).pipe(map(fromJson)).subscribe(next, errorInterceptor(error)); } postReturnList(path: string, data: any, fromJson: (json: any) => T, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) { - this.http.post(ApiService.url("http", path), data).pipe(map(list => list.map(fromJson))).subscribe(next, errorInterceptor(error)); + this.http.post(this.restUrl(path), data).pipe(map(list => list.map(fromJson))).subscribe(next, errorInterceptor(error)); } - private static url(schema: string, path: string): string { - return schema + "://" + environment.host + ":" + environment.port + "/" + path; + private restUrl(path: string): string { + return environment.restBase + this.locationStrategy.getBaseHref() + path; + } + + private websocketUrl(path: string) { + return environment.websocketBase + this.locationStrategy.getBaseHref() + path; } } diff --git a/src/main/angular/src/app/shared/search/search.component.spec.ts b/src/main/angular/src/app/shared/search/search.component.spec.ts index b17eced..3fe0a21 100644 --- a/src/main/angular/src/app/shared/search/search.component.spec.ts +++ b/src/main/angular/src/app/shared/search/search.component.spec.ts @@ -3,8 +3,8 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {SearchComponent} from './search.component'; describe('SearchComponent', () => { - let component: SearchComponent; - let fixture: ComponentFixture; + let component: SearchComponent; + let fixture: ComponentFixture>; beforeEach(async () => { await TestBed.configureTestingModule({ diff --git a/src/main/angular/src/environments/UrlHelper.ts b/src/main/angular/src/environments/UrlHelper.ts new file mode 100644 index 0000000..53ae3e2 --- /dev/null +++ b/src/main/angular/src/environments/UrlHelper.ts @@ -0,0 +1,6 @@ +const secure: boolean = window.location.protocol === "https:"; +const host: string = window.location.host.split(":", 1)[0]; + +export function getBaseUrl(protocol: string, port: number) { + return protocol + (secure ? 's' : '') + "://" + host + ":" + port; +} diff --git a/src/main/angular/src/environments/environment.prod.ts b/src/main/angular/src/environments/environment.prod.ts index 87d85a3..f55fd2c 100644 --- a/src/main/angular/src/environments/environment.prod.ts +++ b/src/main/angular/src/environments/environment.prod.ts @@ -1,5 +1,7 @@ +import {getBaseUrl} from "./UrlHelper"; + export const environment = { production: true, - host: window.location.host.split(":")[0], - port: window.location.port, + restBase: '', + websocketBase: getBaseUrl('ws', parseInt(window.location.port)), }; diff --git a/src/main/angular/src/environments/environment.ts b/src/main/angular/src/environments/environment.ts index fa2442e..66de4ae 100644 --- a/src/main/angular/src/environments/environment.ts +++ b/src/main/angular/src/environments/environment.ts @@ -2,10 +2,12 @@ // `ng build` replaces `environment.ts` with `environment.prod.ts`. // The list of file replacements can be found in `angular.json`. +import {getBaseUrl} from "./UrlHelper"; + export const environment = { production: false, - host: window.location.host.split(":")[0], - port: 8080, + restBase: getBaseUrl('http', 8080), + websocketBase: getBaseUrl('ws', 8080), }; /* diff --git a/src/main/angular/src/index.html b/src/main/angular/src/index.html index d3d0ec5..aa06306 100644 --- a/src/main/angular/src/index.html +++ b/src/main/angular/src/index.html @@ -8,6 +8,6 @@ - + diff --git a/src/main/java/de/ph87/homeautomation/BackendApplication.java b/src/main/java/de/ph87/homeautomation/BackendApplication.java index b8948f1..57ac8dd 100644 --- a/src/main/java/de/ph87/homeautomation/BackendApplication.java +++ b/src/main/java/de/ph87/homeautomation/BackendApplication.java @@ -1,28 +1,14 @@ package de.ph87.homeautomation; -import de.ph87.homeautomation.knx.group.KnxGroupImportService; -import lombok.RequiredArgsConstructor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; - -import javax.annotation.PostConstruct; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication -@RequiredArgsConstructor -public class BackendApplication { - - private final DemoDataService demoDataService; - - private final KnxGroupImportService knxGroupImportService; +public class BackendApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(BackendApplication.class); } - @PostConstruct - public void postConstruct() { - knxGroupImportService.importGroups(); - demoDataService.insertDemoData(); - } - } \ No newline at end of file diff --git a/src/main/java/de/ph87/homeautomation/DemoDataService.java b/src/main/java/de/ph87/homeautomation/DemoDataService.java index 212f708..e908359 100644 --- a/src/main/java/de/ph87/homeautomation/DemoDataService.java +++ b/src/main/java/de/ph87/homeautomation/DemoDataService.java @@ -2,7 +2,6 @@ package de.ph87.homeautomation; import com.luckycatlabs.sunrisesunset.Zenith; import de.ph87.homeautomation.channel.Channel; -import de.ph87.homeautomation.device.DeviceRepository; import de.ph87.homeautomation.device.DeviceWriteService; import de.ph87.homeautomation.device.devices.DeviceDto; import de.ph87.homeautomation.knx.group.KnxGroup; @@ -10,12 +9,10 @@ import de.ph87.homeautomation.knx.group.KnxGroupReadService; import de.ph87.homeautomation.logic.Logic; import de.ph87.homeautomation.logic.LogicOperator; import de.ph87.homeautomation.logic.LogicRepository; -import de.ph87.homeautomation.logic.LogicWriter; import de.ph87.homeautomation.property.Property; import de.ph87.homeautomation.property.PropertyRepository; import de.ph87.homeautomation.property.PropertyType; import de.ph87.homeautomation.scene.SceneDto; -import de.ph87.homeautomation.scene.SceneRepository; import de.ph87.homeautomation.scene.SceneWriteService; import de.ph87.homeautomation.schedule.Schedule; import de.ph87.homeautomation.schedule.ScheduleRepository; @@ -45,17 +42,13 @@ public class DemoDataService { private final DeviceWriteService deviceWriteService; - private final DeviceRepository deviceRepository; - private final PropertyRepository propertyRepository; private final KnxGroupReadService knxGroupReadService; - private final SceneRepository sceneRepository; private final SceneWriteService sceneWriteService; - private final LogicWriter logicWriter; private final LogicRepository logicRepository; @@ -172,7 +165,7 @@ public class DemoDataService { } private KnxGroup knx(final int main, final int mid, final int sub) { - return knxGroupReadService.getByAddress(main, mid, sub); + return knxGroupReadService.getByAddress(main, mid, sub).orElse(null); } private Property createProperty(final String title, final PropertyType type, final Channel readChannel, final Channel writeChannel) { diff --git a/src/main/java/de/ph87/homeautomation/ServerController.java b/src/main/java/de/ph87/homeautomation/ServerController.java deleted file mode 100644 index 022a738..0000000 --- a/src/main/java/de/ph87/homeautomation/ServerController.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.ph87.homeautomation; - -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("server") -@RequiredArgsConstructor -public class ServerController { - - private final ServerService serverService; - - @GetMapping("shutdown") - public void shutdown() { - serverService.shutdown(); - } - -} diff --git a/src/main/java/de/ph87/homeautomation/ServerService.java b/src/main/java/de/ph87/homeautomation/ServerService.java deleted file mode 100644 index 29889ff..0000000 --- a/src/main/java/de/ph87/homeautomation/ServerService.java +++ /dev/null @@ -1,43 +0,0 @@ -package de.ph87.homeautomation; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.stereotype.Service; - -@Slf4j -@Service -@EnableAsync -@RequiredArgsConstructor -public class ServerService implements ApplicationContextAware { - - private static final int DELAY_SECONDS = 1; - - private ConfigurableApplicationContext applicationContext; - - @Async - public void shutdown() { - try { - for (int delay = DELAY_SECONDS; delay > 0; delay--) { - log.info("Shutdown in {} second.", delay); - Thread.sleep(1000); - } - } catch (InterruptedException e) { - log.warn("Shutdown interrupted."); - return; - } - applicationContext.close(); - System.exit(0); - } - - @Override - public void setApplicationContext(ApplicationContext ctx) throws BeansException { - this.applicationContext = (ConfigurableApplicationContext) ctx; - } - -} diff --git a/src/main/java/de/ph87/homeautomation/property/PropertyEventListener.java b/src/main/java/de/ph87/homeautomation/StartupService.java similarity index 52% rename from src/main/java/de/ph87/homeautomation/property/PropertyEventListener.java rename to src/main/java/de/ph87/homeautomation/StartupService.java index 4bd337c..26b66b3 100644 --- a/src/main/java/de/ph87/homeautomation/property/PropertyEventListener.java +++ b/src/main/java/de/ph87/homeautomation/StartupService.java @@ -1,5 +1,7 @@ -package de.ph87.homeautomation.property; +package de.ph87.homeautomation; +import de.ph87.homeautomation.knx.group.KnxGroupImportService; +import de.ph87.homeautomation.property.PropertyWriteService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationStartedEvent; @@ -9,12 +11,18 @@ import org.springframework.stereotype.Service; @Slf4j @Service @RequiredArgsConstructor -public class PropertyEventListener { +public class StartupService { + + private final KnxGroupImportService knxGroupImportService; + + private final DemoDataService demoDataService; private final PropertyWriteService propertyWriteService; @EventListener(ApplicationStartedEvent.class) - public void onApplicationStarted() { + public void startup() { + knxGroupImportService.importGroups(); + demoDataService.insertDemoData(); propertyWriteService.updateAllProperties(); } diff --git a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupReadService.java b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupReadService.java index 7922bd1..113ec1e 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupReadService.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupReadService.java @@ -7,6 +7,7 @@ import org.springframework.transaction.annotation.Transactional; import tuwien.auto.calimero.GroupAddress; import java.util.List; +import java.util.Optional; @Slf4j @Service @@ -16,8 +17,8 @@ public class KnxGroupReadService { private final KnxGroupRepository knxGroupRepository; - public KnxGroup getByAddress(final int main, final int mid, final int sub) { - return knxGroupRepository.findByAddressRaw(new GroupAddress(main, mid, sub).getRawAddress()).orElseThrow(RuntimeException::new); + public Optional getByAddress(final int main, final int mid, final int sub) { + return knxGroupRepository.findByAddressRaw(new GroupAddress(main, mid, sub).getRawAddress()); } public List findAll() { diff --git a/src/main/java/de/ph87/homeautomation/web/WebConfig.java b/src/main/java/de/ph87/homeautomation/web/WebConfig.java index 7b77166..08a6e84 100644 --- a/src/main/java/de/ph87/homeautomation/web/WebConfig.java +++ b/src/main/java/de/ph87/homeautomation/web/WebConfig.java @@ -1,18 +1,9 @@ package de.ph87.homeautomation.web; import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.firewall.HttpFirewall; -import org.springframework.security.web.firewall.StrictHttpFirewall; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -21,34 +12,8 @@ import org.springframework.web.servlet.resource.PathResourceResolver; import java.io.IOException; @Configuration -@EnableWebSecurity @RequiredArgsConstructor -public class WebConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.cors(); - http.csrf().disable(); - http.authorizeRequests().anyRequest().permitAll(); - } - - @Override - public void configure(WebSecurity web) { - web.httpFirewall(allowUrlEncodedSlashHttpFirewall()); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public HttpFirewall allowUrlEncodedSlashHttpFirewall() { - final StrictHttpFirewall firewall = new StrictHttpFirewall(); - firewall.setAllowUrlEncodedSlash(true); - firewall.setAllowSemicolon(true); - return firewall; - } +public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 264df89..f9fffb6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,17 +1,10 @@ logging.level.root=WARN logging.level.de.ph87=INFO #- -spring.datasource.url=jdbc:h2:./Homeautomation;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.username=sa -spring.datasource.password=password -#- spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl spring.jpa.hibernate.ddl-auto=update spring.jpa.open-in-view=false #- -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect -#- spring.jackson.serialization.indent_output=true #- spring.main.banner-mode=off