Merge branch 'refs/heads/komoot-import' into sattelgeschichten

This commit is contained in:
Marcus Fihlon 2026-04-30 12:26:29 +02:00
commit 45517ae1e6
Signed by: McPringle
GPG key ID: C6B7F469EE363E1F
7 changed files with 107 additions and 1 deletions

View file

@ -0,0 +1,21 @@
package net.javahippie.fitpub.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* Central support flag for Komoot integration availability.
*/
@Component
public class KomootSupport {
private final boolean enabled;
public KomootSupport(@Value("${fitpub.komoot.enabled:false}") boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
}

View file

@ -0,0 +1,21 @@
package net.javahippie.fitpub.controller;
import lombok.RequiredArgsConstructor;
import net.javahippie.fitpub.config.KomootSupport;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
/**
* Exposes global model attributes required by shared layouts.
*/
@ControllerAdvice
@RequiredArgsConstructor
public class GlobalModelAttributes {
private final KomootSupport komootSupport;
@ModelAttribute("komootSupportEnabled")
public boolean komootSupportEnabled() {
return komootSupport.isEnabled();
}
}

View file

@ -3,6 +3,7 @@ package net.javahippie.fitpub.controller;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.javahippie.fitpub.config.KomootSupport;
import net.javahippie.fitpub.model.dto.KomootActivityImportRequest;
import net.javahippie.fitpub.model.dto.KomootActivitiesResponse;
import net.javahippie.fitpub.model.dto.KomootImportExecutionResponse;
@ -30,6 +31,7 @@ import java.util.UUID;
@Slf4j
public class KomootImportController {
private final KomootSupport komootSupport;
private final KomootImportService komootImportService;
private final UserRepository userRepository;
@ -38,6 +40,8 @@ public class KomootImportController {
@Valid @RequestBody KomootImportRequest request,
Authentication authentication
) {
ensureKomootSupportEnabled();
UUID fitPubUserId = userRepository.findByUsername(authentication.getName())
.orElseThrow(() -> new IllegalArgumentException("Authenticated user not found"))
.getId();
@ -53,6 +57,8 @@ public class KomootImportController {
@Valid @RequestBody KomootActivityImportRequest request,
Authentication authentication
) {
ensureKomootSupportEnabled();
UUID fitPubUserId = userRepository.findByUsername(authentication.getName())
.orElseThrow(() -> new IllegalArgumentException("Authenticated user not found"))
.getId();
@ -67,6 +73,12 @@ public class KomootImportController {
return ResponseEntity.ok(response);
}
private void ensureKomootSupportEnabled() {
if (!komootSupport.isEnabled()) {
throw new KomootSupportDisabledException();
}
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage()));
@ -86,5 +98,13 @@ public class KomootImportController {
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body(new ErrorResponse(e.getMessage()));
}
@ExceptionHandler(KomootSupportDisabledException.class)
public ResponseEntity<ErrorResponse> handleKomootSupportDisabled(KomootSupportDisabledException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse("Komoot support is disabled."));
}
record ErrorResponse(String error) {}
static class KomootSupportDisabledException extends RuntimeException {
}
}

View file

@ -1,5 +1,7 @@
package net.javahippie.fitpub.controller;
import lombok.RequiredArgsConstructor;
import net.javahippie.fitpub.config.KomootSupport;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@ -10,10 +12,21 @@ import java.time.LocalDate;
* Serves the Komoot import preview page.
*/
@Controller
@RequiredArgsConstructor
public class KomootImportViewController {
private final KomootSupport komootSupport;
@GetMapping("/komoot-import")
public String komootImportPage(Model model) {
if (!komootSupport.isEnabled()) {
model.addAttribute("pageTitle", "Komoot Import Unavailable");
model.addAttribute("featureName", "Komoot Import");
model.addAttribute("featureMessage", "Komoot support is currently disabled on this instance.");
model.addAttribute("featureIcon", "bi bi-signpost-split text-secondary");
return "feature-disabled";
}
LocalDate today = LocalDate.now();
model.addAttribute("pageTitle", "Komoot Import");
model.addAttribute("defaultStartDate", today.withDayOfYear(1));

View file

@ -104,7 +104,9 @@ fitpub:
enabled: ${WEATHER_ENABLED:false}
api-key: ${OPENWEATHERMAP_API_KEY:}
# Komoot settings
komoot:
enabled: ${KOMOOT_ENABLED:false}
base-url: ${KOMOOT_BASE_URL:https://www.komoot.com}
paginated-request-delay-ms: ${KOMOOT_PAGINATED_REQUEST_DELAY_MS:1000}
detail-to-gpx-delay-ms: ${KOMOOT_DETAIL_TO_GPX_DELAY_MS:500}

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title th:text="${pageTitle}">Feature Unavailable</title>
</head>
<body>
<div layout:fragment="content">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="text-center mb-4">
<h2 class="mb-3">
<i th:class="${featureIcon != null ? featureIcon : 'bi bi-slash-circle text-secondary'}"></i>
<span th:text="${featureName != null ? featureName : 'Feature'}">Feature</span>
</h2>
<p class="text-muted mb-0"
th:text="${featureMessage != null ? featureMessage : 'This feature is currently unavailable on this FitPub instance.'}">
This feature is currently unavailable on this FitPub instance.
</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -97,7 +97,7 @@
<i class="bi bi-file-earmark-zip"></i> Batch Import
</a>
</li>
<li>
<li th:if="${komootSupportEnabled}">
<a class="dropdown-item" th:href="@{/komoot-import}">
<i class="bi bi-signpost-split"></i> Komoot Import
</a>