-
-
Notifications
You must be signed in to change notification settings - Fork 464
Add webhook plugin integration and configuration changes #1983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| /target | ||
| /.idea | ||
| *.iml | ||
| .DS_Store | ||
| .DS_Store.env |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ db.ip = localhost | |
| db.port = 3306 | ||
| db.schema = stevedb | ||
| db.user = steve | ||
| db.password = changeme | ||
| db.password = StevePass2026! | ||
|
|
||
| # Credentials for Web interface access | ||
| # | ||
|
|
@@ -20,18 +20,26 @@ auth.password = 1234 | |
| # The header key and value for Web API access using API key authorization. | ||
| # Both must be set for Web APIs to be enabled. Otherwise, we will block all calls. | ||
| # | ||
| webapi.key = STEVE-API-KEY | ||
| webapi.value = | ||
| #webapi.key = STEVE-API-KEY | ||
| #webapi.value = | ||
|
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4. Unresolved webapi placeholders In prod, webapi.key and webapi.value are commented out, but application.yml still requires
${webapi.key}/${webapi.value}, which can prevent Spring from resolving placeholders and stop SteVe
from starting in the prod profile.
Agent Prompt
|
||
|
|
||
| # ───────────────────────────────────────────────────── | ||
| # REST API CONFIGURATION (ADD THESE) | ||
| # ───────────────────────────────────────────────────── | ||
| rest.enabled=true | ||
| rest.api.path=/steve/api/v1 | ||
|
|
||
| # Jetty configuration | ||
| # | ||
| server.host = 127.0.0.1 | ||
| #server.host = 0.0.0.0 | ||
| server.address = 0.0.0.0 | ||
| server.port = 8080 | ||
| server.gzip.enabled = true | ||
|
Comment on lines
+34
to
37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 5. Prod service exposed publicly application-prod.properties binds the server to 0.0.0.0 and enables extensive DEBUG logging, increasing exposure of the admin interface and sensitive operational data in logs. Agent Prompt
|
||
|
|
||
| # Jetty HTTP configuration | ||
| # | ||
| http.enabled = true | ||
| http.port = 8080 | ||
| #http.enabled = true | ||
| #http.port = 8080 | ||
|
|
||
| # Jetty HTTPS configuration | ||
| # | ||
|
|
@@ -40,6 +48,11 @@ https.port = 8443 | |
| keystore.path = | ||
| keystore.password = | ||
|
|
||
| # OCPP WebSocket Server (CRITICAL for chargers) | ||
| # | ||
| ocpp.j.server.port = 8880 | ||
| ocpp.j.server.host = 0.0.0.0 | ||
|
|
||
| # When the WebSocket/Json charge point opens more than one WebSocket connection, | ||
| # we need a mechanism/strategy to select one of them for outgoing requests. | ||
| # For allowed values see de.rwth.idsg.steve.ocpp.ws.custom.WsSessionSelectStrategyEnum. | ||
|
|
@@ -64,3 +77,22 @@ charge-box-id.validation.regex = | |
| ### DO NOT MODIFY ### | ||
| db.sql.logging = false | ||
| profile = prod | ||
|
|
||
| # SteVe Webhook Plugin Environment Variables | ||
| voltstartev.webhook.url=http://localhost:3000/api/webhooks/steve | ||
| voltstartev.webhook.secret=your-super-secret-webhook-key-min-32-characters | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2. voltstartev.webhook.secret in prod The production configuration adds a webhook shared secret value directly in the properties file. Storing shared secrets in plaintext configuration violates secret storage hardening and increases leak risk. Agent Prompt
|
||
| voltstartev.webhook.connect-timeout=3000 | ||
| voltstartev.webhook.read-timeout=5000 | ||
| voltstartev.webhook.retry-max-attempts=3 | ||
| voltstartev.webhook.meter-values-sampling-enabled=true | ||
| voltstartev.webhook.meter-values-min-interval-seconds=30 | ||
| # ───────────────────────────────────────────────────── | ||
| # 🔍 DEBUG LOGGING (Enable for troubleshooting) | ||
| # ───────────────────────────────────────────────────── | ||
| logging.level.de.rwth.idsg.steve=DEBUG | ||
| logging.level.de.rwth.idsg.steve.ocpp=DEBUG | ||
| logging.level.de.rwth.idsg.steve.ocpp.ws=DEBUG | ||
| logging.level.de.rwth.idsg.steve.service.reservation=DEBUG | ||
| logging.level.de.rwth.idsg.steve.service.transaction=DEBUG | ||
| logging.level.de.rwth.idsg.steve.web.api=DEBUG | ||
| logging.level.de.rwth.idsg.steve.ws=DEBUG | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #!/bin/bash | ||
|
|
||
| HEADER_FILE="steve-header.txt" | ||
|
|
||
| find . -name "*.java" | while read file; do | ||
| if ! grep -q "Licensed under the Apache License" "$file"; then | ||
| echo "Adding header to $file" | ||
| cat "$HEADER_FILE" "$file" > temp && mv temp "$file" | ||
| else | ||
| echo "Header already exists in $file" | ||
| fi | ||
| done | ||
|
|
||
| echo "License headers added." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| <?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> | ||
| </parent> | ||
|
|
||
| <groupId>com.voltstartev</groupId> | ||
| <artifactId>steve-webhook-plugin</artifactId> | ||
| <version>1.0.0</version> | ||
| <packaging>jar</packaging> | ||
|
|
||
| <dependencies> | ||
| <!-- Spring Boot --> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-web</artifactId> | ||
| </dependency> | ||
|
|
||
| <!-- HTTP Client with connection pooling --> | ||
| <dependency> | ||
| <groupId>org.apache.httpcomponents.client5</groupId> | ||
| <artifactId>httpclient5</artifactId> | ||
| <version>5.2.1</version> | ||
| </dependency> | ||
|
|
||
| <!-- JSON Processing --> | ||
| <dependency> | ||
| <groupId>com.fasterxml.jackson.core</groupId> | ||
| <artifactId>jackson-databind</artifactId> | ||
| </dependency> | ||
|
|
||
| <!-- Metrics (optional) --> | ||
| <dependency> | ||
| <groupId>io.micrometer</groupId> | ||
| <artifactId>micrometer-registry-prometheus</artifactId> | ||
| </dependency> | ||
|
|
||
| <!-- SteVe Core (provided by SteVe runtime) --> | ||
| <dependency> | ||
| <groupId>de.rwth.idsg</groupId> | ||
| <artifactId>steve</artifactId> | ||
| <version>3.11.0</version> | ||
| <scope>provided</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
Comment on lines
+19
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 6. Missing lombok dependency steve-webhook-plugin uses Lombok annotations (@Data, @AllArgsConstructor, etc.) but does not declare Lombok in its pom.xml, which will fail compilation of the plugin module. Agent Prompt
|
||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-maven-plugin</artifactId> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /* | ||
| * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve | ||
| * Copyright (C) 2013-2026 SteVe Community Team | ||
| * Copyright (C) 2026 VoltStar | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| */ | ||
| package com.voltstartev.steve.plugin; | ||
|
|
||
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| import org.springframework.scheduling.annotation.EnableScheduling; | ||
|
|
||
| @SpringBootApplication | ||
| @EnableScheduling // For retry worker | ||
| public class SteveWebhookPluginApplication { | ||
| public static void main(String[] args) { | ||
| SpringApplication.run(SteveWebhookPluginApplication.class, args); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /* | ||
| * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve | ||
| * Copyright (C) 2013-2026 SteVe Community Team | ||
| * Copyright (C) 2026 VoltStar | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| */ | ||
| package com.voltstartev.steve.plugin.config; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.scheduling.annotation.EnableAsync; | ||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||
|
|
||
| import java.util.concurrent.Executor; | ||
|
|
||
| @Configuration | ||
| @EnableAsync | ||
| public class AsyncConfig { | ||
|
|
||
| private final WebhookProperties properties; | ||
|
|
||
| public AsyncConfig(WebhookProperties properties) { | ||
| this.properties = properties; | ||
| } | ||
|
|
||
| @Bean(name = "webhookExecutor") | ||
| public Executor webhookExecutor() { | ||
| ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); | ||
| executor.setCorePoolSize(10); | ||
| executor.setMaxPoolSize(20); | ||
| executor.setQueueCapacity(100); | ||
| executor.setThreadNamePrefix("webhook-"); | ||
| executor.setRejectedExecutionHandler( | ||
| new ThreadPoolExecutor.CallerRunsPolicy() // Backpressure: run in caller thread if queue full | ||
| ); | ||
|
Comment on lines
+34
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 7. Missing imports break plugin steve-webhook-plugin references ThreadPoolExecutor and WebhookProperties without importing them, which prevents the module from compiling. Agent Prompt
|
||
| executor.setWaitForTasksToCompleteOnShutdown(true); | ||
| executor.setAwaitTerminationSeconds(30); | ||
| executor.initialize(); | ||
| return executor; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| /* | ||
| * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve | ||
| * Copyright (C) 2013-2026 SteVe Community Team | ||
| * Copyright (C) 2026 VoltStar | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| */ | ||
| package com.voltstartev.steve.plugin.config; | ||
|
|
||
| import org.apache.hc.client5.http.classic.HttpClient; | ||
| import org.apache.hc.client5.http.config.RequestConfig; | ||
| import org.apache.hc.client5.http.impl.classic.HttpClients; | ||
| import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; | ||
| import org.apache.hc.core5.util.Timeout; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; | ||
| import org.springframework.web.client.RestTemplate; | ||
|
|
||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| @Configuration | ||
| public class HttpClientConfig { | ||
|
|
||
| private final WebhookProperties properties; | ||
|
|
||
| public HttpClientConfig(WebhookProperties properties) { | ||
| this.properties = properties; | ||
| } | ||
|
|
||
| @Bean | ||
| public RestTemplate restTemplate() { | ||
| // Connection pooling | ||
| PoolingHttpClientConnectionManager connectionManager = | ||
| new PoolingHttpClientConnectionManager(); | ||
| connectionManager.setMaxTotal(properties.getMaxConnections()); | ||
| connectionManager.setDefaultMaxPerRoute(properties.getMaxConnectionsPerRoute()); | ||
| connectionManager.setValidateAfterInactivity(5000); | ||
|
|
||
| // Request config | ||
| RequestConfig requestConfig = RequestConfig.custom() | ||
| .setConnectTimeout(Timeout.ofMilliseconds(properties.getConnectTimeout())) | ||
| .setResponseTimeout(Timeout.ofMilliseconds(properties.getReadTimeout())) | ||
| .build(); | ||
|
|
||
| // Build HTTP client | ||
| HttpClient httpClient = HttpClients.custom() | ||
| .setConnectionManager(connectionManager) | ||
| .setDefaultRequestConfig(requestConfig) | ||
| .evictIdleConnections(30, TimeUnit.SECONDS) | ||
| .build(); | ||
|
|
||
| // Create RestTemplate with factory | ||
| HttpComponentsClientHttpRequestFactory factory = | ||
| new HttpComponentsClientHttpRequestFactory(httpClient); | ||
|
|
||
| return new RestTemplate(factory); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /* | ||
| * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve | ||
| * Copyright (C) 2013-2026 SteVe Community Team | ||
| * Copyright (C) 2026 VoltStar | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| */ | ||
| package com.voltstartev.steve.plugin.config; | ||
|
|
||
| import lombok.Data; | ||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Data | ||
| @Component | ||
| @ConfigurationProperties(prefix = "voltstartev.webhook") | ||
| public class WebhookProperties { | ||
| private String url = "http://localhost:3000/api/webhooks/steve"; | ||
| private String secret = ""; // Shared secret for HMAC signature | ||
| private int connectTimeout = 3000; | ||
| private int readTimeout = 5000; | ||
| private int maxConnections = 50; | ||
| private int maxConnectionsPerRoute = 20; | ||
| private int retryMaxAttempts = 3; | ||
| private long retryInitialDelayMs = 500; | ||
| private boolean meterValuesSamplingEnabled = true; | ||
| private int meterValuesMinEnergyDeltaWh = 100; // Only send if energy changed by >100Wh | ||
| private int meterValuesMinIntervalSeconds = 30; // Or if >30s since last send | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| /* | ||
| * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve | ||
| * Copyright (C) 2013-2026 SteVe Community Team | ||
| * Copyright (C) 2026 VoltStar | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| */ | ||
| package com.voltstartev.steve.plugin.events; | ||
|
|
||
| import lombok.AllArgsConstructor; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| import java.time.Instant; | ||
|
|
||
| @Data | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class ConnectorStatusEvent implements SteveWebhookEvent { | ||
| private String chargeBoxId; | ||
| private Integer connectorId; | ||
| private String status; // OCPP ConnectorStatus enum value | ||
| private String errorCode; | ||
| private String info; | ||
| private Instant eventTimestamp; // From OCPP message | ||
| private Instant receivedAt; // When SteVe received it | ||
|
|
||
| @Override | ||
| public String getChargeBoxId() { return chargeBoxId; } | ||
|
|
||
| @Override | ||
| public Integer getConnectorId() { return connectorId; } | ||
|
|
||
| @Override | ||
| public Instant getEventTimestamp() { return eventTimestamp; } | ||
|
|
||
| @Override | ||
| public Instant getReceivedAt() { return receivedAt; } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. db.password plaintext in prod
📘 Rule violation⛨ SecurityAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools