| author | Thorsten Ortlepp
<post@ortlepp.ms> 2025-12-10 22:05:41 UTC |
| committer | Thorsten Ortlepp
<post@ortlepp.ms> 2025-12-10 22:05:41 UTC |
| parent | b94f50c57438dc4d820618eb651e1c0e9056d12c |
| LICENSE.md | +20 | -0 |
| README.md | +3 | -3 |
| pom.xml | +20 | -16 |
| src/main/java/dev/rubidium/subscriptiontool/SubscriptionToolApplication.java | +1 | -1 |
| src/main/java/dev/rubidium/subscriptiontool/configuration/SecurityConfiguration.java | +0 | -2 |
| src/main/java/dev/rubidium/subscriptiontool/controller/SubscriptionController.java | +0 | -6 |
| src/main/java/dev/rubidium/subscriptiontool/service/impl/PersistenceServiceImpl.java | +11 | -7 |
| src/main/resources/application.properties | +2 | -0 |
diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..b87f10e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,20 @@ +# MIT License + +Copyright 2025 Thorsten Ortlepp + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the “Software”), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[https://opensource.org/license/mit](https://opensource.org/license/mit) diff --git a/README.md b/README.md index 2d643d3..21144e7 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ CREATE DATABASE subscriptiontool; ### Configuration Basic authentication protects private endpoints (e.g., `/actuator`). The username and password can - be configured by passing the following JVM parameters: + be configured by using the following properties: -- `-Dsubscriptiontool.web.username=USERNAME` -- `-Dsubscriptiontool.web.password=PASSWORD` +- `subscriptiontool.web.username=USERNAME` +- `subscriptiontool.web.password=PASSWORD` The password is hashed using Bcrypt. To create a password hash, use the following command: `mkpasswd -m bcrypt` (on Debian GNU/Linux `mkpasswd` is part of the *whois* package). diff --git a/pom.xml b/pom.xml index 525209a..862bb95 100644 --- a/pom.xml +++ b/pom.xml @@ -9,27 +9,19 @@ <version>4.0.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> + <groupId>dev.rubidium</groupId> <artifactId>subscription-tool</artifactId> - <version>0.0.1-SNAPSHOT</version> + <version>0.1</version> + <packaging>jar</packaging> <name>subscription-tool</name> - <description>subscription-tool</description> - <url/> - <licenses> - <license/> - </licenses> - <developers> - <developer/> - </developers> - <scm> - <connection/> - <developerConnection/> - <tag/> - <url/> - </scm> + <description>a tool to manage subscriptions (e.g., to a newsletter)</description> + <url>https://git.ortlepp.ms/web/r/subscription-tool.git/</url> + <properties> <java.version>25</java.version> </properties> + <dependencies> <dependency> <groupId>org.springframework.boot</groupId> @@ -67,7 +59,6 @@ <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity6</artifactId> </dependency> - <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> @@ -115,6 +106,19 @@ <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + <configuration> + <classifier>spring-boot</classifier> + <mainClass> + dev.rubidium.subscriptiontool.SubscriptionToolApplication + </mainClass> + </configuration> + </execution> + </executions> </plugin> </plugins> </build> diff --git a/src/main/java/dev/rubidium/subscriptiontool/SubscriptionToolApplication.java b/src/main/java/dev/rubidium/subscriptiontool/SubscriptionToolApplication.java index a61eab7..2720e0b 100644 --- a/src/main/java/dev/rubidium/subscriptiontool/SubscriptionToolApplication.java +++ b/src/main/java/dev/rubidium/subscriptiontool/SubscriptionToolApplication.java @@ -12,7 +12,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling public class SubscriptionToolApplication { - public static void main(String[] args) { + static void main(String[] args) { SpringApplication.run(SubscriptionToolApplication.class, args); } diff --git a/src/main/java/dev/rubidium/subscriptiontool/configuration/SecurityConfiguration.java b/src/main/java/dev/rubidium/subscriptiontool/configuration/SecurityConfiguration.java index 6238d78..624f73c 100644 --- a/src/main/java/dev/rubidium/subscriptiontool/configuration/SecurityConfiguration.java +++ b/src/main/java/dev/rubidium/subscriptiontool/configuration/SecurityConfiguration.java @@ -24,7 +24,6 @@ public class SecurityConfiguration { @Value("${subscriptiontool.web.password}") private String password; - @Bean public SecurityFilterChain defaultHttpSecurity(HttpSecurity httpSecurity) { httpSecurity @@ -37,7 +36,6 @@ public class SecurityConfiguration { return httpSecurity.build(); } - @Bean public UserDetailsService users() { UserDetails user = User.builder() diff --git a/src/main/java/dev/rubidium/subscriptiontool/controller/SubscriptionController.java b/src/main/java/dev/rubidium/subscriptiontool/controller/SubscriptionController.java index 31697e2..92d68f3 100644 --- a/src/main/java/dev/rubidium/subscriptiontool/controller/SubscriptionController.java +++ b/src/main/java/dev/rubidium/subscriptiontool/controller/SubscriptionController.java @@ -27,14 +27,12 @@ public class SubscriptionController { this.translation = translation; } - @GetMapping("/") public String index(Model model) { model.addAttribute(ATTRIBUTE_NAME_TRANSLATION, translation); return "Index"; } - @GetMapping("/subscribe") public String subscribeForm(Model model) { model.addAttribute(ATTRIBUTE_NAME_SUBSCRIPTION, new Subscription()); @@ -42,7 +40,6 @@ public class SubscriptionController { return "SubscribeForm"; } - @PostMapping("/subscribe") public String subscribeSave(@ModelAttribute Subscription subscription, Model model) { boolean saved = false; @@ -56,7 +53,6 @@ public class SubscriptionController { return "SubscribeSave"; } - @GetMapping("/unsubscribe") public String unsubscribeForm(Model model) { model.addAttribute(ATTRIBUTE_NAME_SUBSCRIPTION, new Subscription()); @@ -64,7 +60,6 @@ public class SubscriptionController { return "UnsubscribeForm"; } - @PostMapping("/unsubscribe") public String unsubscribeDelete(@ModelAttribute Subscription subscription, Model model) { boolean deleted = false; @@ -78,7 +73,6 @@ public class SubscriptionController { return "UnsubscribeDelete"; } - @GetMapping("/confirm") public String confirm(@RequestParam(name = "code") String code, Model model) { boolean confirmed = false; diff --git a/src/main/java/dev/rubidium/subscriptiontool/service/impl/PersistenceServiceImpl.java b/src/main/java/dev/rubidium/subscriptiontool/service/impl/PersistenceServiceImpl.java index 845c82c..d3f7e9d 100644 --- a/src/main/java/dev/rubidium/subscriptiontool/service/impl/PersistenceServiceImpl.java +++ b/src/main/java/dev/rubidium/subscriptiontool/service/impl/PersistenceServiceImpl.java @@ -72,9 +72,13 @@ public class PersistenceServiceImpl implements PersistenceService { public boolean updateSubscription(String code) { Subscription subscription = subscriptionRepository.findByCode(code); if (subscription != null) { - subscription.setConfirmed(Boolean.TRUE); - subscription.setConfirmation(Timestamp.valueOf(LocalDateTime.now())); - subscriptionRepository.saveAndFlush(subscription); + if (subscription.getConfirmed()) { + logger.warn("Subscription {} already confirmed", subscription.getId()); + } else { + subscription.setConfirmed(Boolean.TRUE); + subscription.setConfirmation(Timestamp.valueOf(LocalDateTime.now())); + subscriptionRepository.saveAndFlush(subscription); + } return true; } logger.warn("No subscription found for code {}", code); @@ -84,10 +88,10 @@ public class PersistenceServiceImpl implements PersistenceService { @Override public List<UnsentMail> getUnsentMails() { var mails = new ArrayList<UnsentMail>(); - mailRepository.findUnsentMails().forEach(mail -> { - subscriptionRepository.findById(mail.getSubscription()).ifPresent(subscription -> - mails.add(new UnsentMail(mail.getId(), subscription.getMail(), subscription.getCode()))); - }); + mailRepository.findUnsentMails().forEach(mail -> + subscriptionRepository.findById(mail.getSubscription()).ifPresent(subscription -> + mails.add(new UnsentMail(mail.getId(), subscription.getMail(), + subscription.getCode())))); return mails; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 778e402..b875956 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -30,5 +30,7 @@ subscriptiontool.mail.from=hello@example.com subscriptiontool.mail.url=https://example.com/confirm?code= subscriptiontool.scheduler.cron=0 * * * * * subscriptiontool.scheduler.zone=Europe/Berlin +subscriptiontool.web.username=USERNAME +subscriptiontool.web.password=PASSWORD spring.config.import=translation.properties