From: Ole B. Rosentreter Date: Mon, 20 Apr 2026 09:21:11 +0000 (+0200) Subject: erster Entwurf by Claude-KI X-Git-Url: https://git.laktatnebel.de/?a=commitdiff_plain;p=service.triathlon-coaching.com.git erster Entwurf by Claude-KI --- diff --git a/base-auth/pom.xml b/base-auth/pom.xml new file mode 100644 index 0000000..959835e --- /dev/null +++ b/base-auth/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + base-auth + Base — Auth (Benutzerverwaltung, JWT, 2FA) + + + + com.triathlon-coaching.product + base-core + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-mail + + + io.jsonwebtoken + jjwt-api + + + io.jsonwebtoken + jjwt-impl + + + io.jsonwebtoken + jjwt-jackson + + + dev.samstevens.totp + totp-spring-boot-starter + + + diff --git a/base-auth/src/main/java/com/triathlon_coaching/product/auth/service/AuthService.java b/base-auth/src/main/java/com/triathlon_coaching/product/auth/service/AuthService.java new file mode 100644 index 0000000..8e7f51c --- /dev/null +++ b/base-auth/src/main/java/com/triathlon_coaching/product/auth/service/AuthService.java @@ -0,0 +1,51 @@ +package com.triathlon_coaching.product.auth.service; + +/** + * Benutzerverwaltung — plattformübergreifend nutzbar für alle Apps. + * + * Flows: + * Registrierung → E-Mail-Bestätigung → aktiver Account + * Login → JWT (30min) + Session-Token (30 Tage, DB-persistiert) + * 2FA → TOTP-Setup → QR-Code → Verify → aktiviert + * Passwort → Token per Mail → Reset + * Entitlement → Produkt-Freischaltung (z.B. 'swim_fit') + */ +public interface AuthService { + + /** Registrierung: sendet Bestätigungsmail. */ + void register(String email, String password, String displayName); + + /** E-Mail-Token einlösen → Account aktiv. */ + void verifyEmail(String token); + + /** + * Login. + * @param totpCode null wenn 2FA nicht aktiv + * @return Session-Token (opakes, DB-persistiertes Token) + */ + String login(String email, String password, String totpCode); + + /** Session invalidieren. */ + void logout(String sessionToken); + + /** Passwort-Reset-Mail anfordern. */ + void requestPasswordReset(String email); + + /** Passwort-Reset-Token einlösen. */ + void resetPassword(String token, String newPassword); + + /** + * TOTP-Setup starten. + * @return otpauth://-URI für QR-Code-Generierung + */ + String setupTotp(String userId); + + /** TOTP aktivieren (nach QR-Code-Scan und erstem Code). */ + void enableTotp(String userId, String totpCode); + + /** TOTP deaktivieren (Passwortbestätigung erforderlich). */ + void disableTotp(String userId, String currentPassword); + + /** Prüft ob User ein Produkt-Entitlement hat (z.B. 'swim_fit'). */ + boolean hasEntitlement(String userId, String productId); +} diff --git a/base-calc/pom.xml b/base-calc/pom.xml new file mode 100644 index 0000000..55ac927 --- /dev/null +++ b/base-calc/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + base-calc + Base — Calc (CSS, Zonen, Jack Daniels, GPX) + + + + com.triathlon-coaching.product + base-core + + + + io.jenetics + jpx + 3.2.0 + + + diff --git a/base-calc/src/main/java/com/triathlon_coaching/product/calc/swim/CssCalculator.java b/base-calc/src/main/java/com/triathlon_coaching/product/calc/swim/CssCalculator.java new file mode 100644 index 0000000..cf092f2 --- /dev/null +++ b/base-calc/src/main/java/com/triathlon_coaching/product/calc/swim/CssCalculator.java @@ -0,0 +1,93 @@ +package com.triathlon_coaching.product.calc.swim; + +import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * Berechnet CSS-basierte Schwimmtempi. + * Direkter Port von swim_lib.inc.php: getZone(), getTempo(), speed2time(), toMin2() + * + * CSS = Critical Swim Speed, angegeben als Sekunden pro 100m. + */ +@Service +public class CssCalculator { + + /** + * CSS-Näherung aus 400m-Test. + * Entspricht: $testspeed = $strecke / $testzeit (mit strecke=400) + */ + public double cssAusTest400(double test400Sek) { + return runden2(test400Sek / 4.0); + } + + /** + * Präzise CSS nach Wakayoshi (1992): aus 200m + 400m Test. + * CSS = (400 - 200) / (t400 - t200) ergibt m/s → umrechnen auf sek/100m + */ + public double cssAusTest200Und400(double t200Sek, double t400Sek) { + double speedMps = (400.0 - 200.0) / (t400Sek - t200Sek); + return runden2(100.0 / speedMps); // Sekunden pro 100m + } + + /** + * Zielzeit für eine Strecke bei gegebenem CSS-Prozentsatz. + * Entspricht: speed2time($testspeed, $level, $distance) + * + * @param cssSek CSS in Sek/100m + * @param cssProzent Zonenintensität, z.B. 85 für 85% CSS + * @param streckeM Streckenlänge in Metern + * @return Zielzeit in Sekunden + */ + public double zielzeit(double cssSek, int cssProzent, int streckeM) { + double speedMps = (100.0 / cssSek) * (cssProzent / 100.0); + return runden2(streckeM / speedMps); + } + + /** + * Zielzeit für Offset-Tempo (+5s, -5s etc. pro 100m). + * Entspricht: getTempo($delta, ...) mit: $testzeit = $testzeit*100/$strecke + $delta + * + * @param cssSek CSS in Sek/100m + * @param deltaSek Offset in Sekunden pro 100m (positiv = langsamer) + * @param streckeM Streckenlänge in Metern + * @return Zielzeit in Sekunden + */ + public double zeitzielMitOffset(double cssSek, int deltaSek, int streckeM) { + double tempoPro100m = cssSek + deltaSek; + return runden2(tempoPro100m * streckeM / 100.0); + } + + /** + * Formatiert Sekunden als "M:SS"-String. + * Entspricht: toMin2($sekunden) in swim_lib.inc.php + */ + public String formatMinSek(double sek) { + int minuten = (int) Math.floor(sek / 60); + double sekRest = Math.round((sek - minuten * 60.0) * 100.0) / 100.0; + if (sekRest >= 60) { + minuten++; + sekRest = 0; + } + return String.format("%d:%s", minuten, + sekRest < 10 + ? String.format("0%.0f", sekRest) + : String.format("%.0f", sekRest)); + } + + /** + * Extrahiert Sekundenanteil als String (für Pausenangabe im PDF). + * Entspricht: getSecondsFromTime($time) — gibt "SS''" zurück. + */ + public String formatPause(String timeStr) { + if (timeStr == null || timeStr.isBlank()) return ""; + if (timeStr.length() >= 8) { + return timeStr.substring(6, 8) + "''"; + } + return timeStr; + } + + private double runden2(double val) { + return BigDecimal.valueOf(val).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/base-core/pom.xml b/base-core/pom.xml new file mode 100644 index 0000000..698a123 --- /dev/null +++ b/base-core/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + base-core + Base — Core (DB-Konfiguration, Basisklassen) + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.postgresql + postgresql + runtime + + + org.flywaydb + flyway-core + + + org.flywaydb + flyway-database-postgresql + + + org.springframework.boot + spring-boot-starter-validation + + + diff --git a/base-core/src/main/resources/db/migration/V1__initial_schema.sql b/base-core/src/main/resources/db/migration/V1__initial_schema.sql new file mode 100644 index 0000000..0c14478 --- /dev/null +++ b/base-core/src/main/resources/db/migration/V1__initial_schema.sql @@ -0,0 +1,8 @@ +-- V1__initial_schema.sql +-- Inhalt: vollständiges Schema aus schema_swim_triathlon_coaching.sql +-- +-- Dieses File muss vor dem ersten Start mit dem Inhalt von +-- schema_swim_triathlon_coaching.sql befüllt werden. +-- +-- Flyway führt alle V*__*.sql Files in der Reihenfolge ihrer Versionsnummer aus. +-- Weitere Migrationen: V2__..., V3__... etc. diff --git a/base-user/pom.xml b/base-user/pom.xml new file mode 100644 index 0000000..37b06bb --- /dev/null +++ b/base-user/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + base-user + Base — User (Profil, Entitlements, CSS-Historie) + + + + com.triathlon-coaching.product + base-core + + + com.triathlon-coaching.product + base-auth + + + diff --git a/pom.xml b/pom.xml index a31f1c8..af845de 100644 --- a/pom.xml +++ b/pom.xml @@ -1,17 +1,157 @@ - - 4.0.0 - de.laktatnebel.product - service.triathlon-coaching.com - 0.0.1-SNAPSHOT - pom + + + 4.0.0 - service.triathlon-coaching.com + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + pom + Triathlon Coaching — Service Parent - - de.laktatnebel.maven - spring-boot-starter-parent - 3.3.1 - + + org.springframework.boot + spring-boot-starter-parent + 3.3.1 + + + + + base-core + base-auth + base-calc + base-user + + swim-domain + swim-pdf + swim-fit + swim-web + + + + 21 + 0.12.5 + 3.0.2 + 1.7.1 + 1.5.5.Final + + + + + + + com.triathlon-coaching.product + base-core + ${project.version} + + + com.triathlon-coaching.product + base-auth + ${project.version} + + + com.triathlon-coaching.product + base-calc + ${project.version} + + + com.triathlon-coaching.product + base-user + ${project.version} + + + + com.triathlon-coaching.product + swim-domain + ${project.version} + + + com.triathlon-coaching.product + swim-pdf + ${project.version} + + + com.triathlon-coaching.product + swim-fit + ${project.version} + + + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jjwt.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + runtime + + + + org.apache.pdfbox + pdfbox + ${pdfbox.version} + + + + dev.samstevens.totp + totp-spring-boot-starter + ${totp.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + + + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + + diff --git a/swim-domain/pom.xml b/swim-domain/pom.xml new file mode 100644 index 0000000..23134c9 --- /dev/null +++ b/swim-domain/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + swim-domain + Swim — Domain (Entities, Repositories, Generator) + + + + com.triathlon-coaching.product + base-core + + + com.triathlon-coaching.product + base-calc + + + com.triathlon-coaching.product + base-user + + + diff --git a/swim-domain/src/main/java/com/triathlon_coaching/product/swim/domain/Block.java b/swim-domain/src/main/java/com/triathlon_coaching/product/swim/domain/Block.java new file mode 100644 index 0000000..08365df --- /dev/null +++ b/swim-domain/src/main/java/com/triathlon_coaching/product/swim/domain/Block.java @@ -0,0 +1,61 @@ +package com.triathlon_coaching.product.swim.domain; + +import jakarta.persistence.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import java.util.ArrayList; +import java.util.List; + +@Data +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Entity +@Table(name = "block", schema = "swim") +public class Block { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @EqualsAndHashCode.Include + private Integer id; + + @Column(nullable = false, unique = true, length = 128) + private String name; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_blockfunktion", nullable = false) + private Blockfunktion blockfunktion; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_trainingsziel") + private Trainingsziel trainingsziel; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_technikschwerpunkt") + private Technikschwerpunkt technikschwerpunkt; + + private String detail; + + /** + * 'serie' | 'pyramide' | 'intervall' | 'technik_drill' + * Pyramiden und Intervallmuster: strecken_muster enthält die Abfolge. + */ + private String strukturtyp; + + /** + * JSON-Array der Streckenlängen für Pyramiden/Intervalle. + * Beispiel Pyramide: [100, 150, 200, 150, 100] + * Beispiel Intervall: [100, 200, 100, 200, 100] + */ + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "strecken_muster", columnDefinition = "jsonb") + private List streckenMuster; + + @OneToMany(mappedBy = "block", + cascade = CascadeType.ALL, + orphanRemoval = true, + fetch = FetchType.LAZY) + @OrderBy("position ASC") + private List uebungen = new ArrayList<>(); +} diff --git a/swim-domain/src/main/java/com/triathlon_coaching/product/swim/domain/Trainingsplan.java b/swim-domain/src/main/java/com/triathlon_coaching/product/swim/domain/Trainingsplan.java new file mode 100644 index 0000000..29011b5 --- /dev/null +++ b/swim-domain/src/main/java/com/triathlon_coaching/product/swim/domain/Trainingsplan.java @@ -0,0 +1,46 @@ +package com.triathlon_coaching.product.swim.domain; + +import jakarta.persistence.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Data +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Entity +@Table(name = "trainingsplan", schema = "swim") +public class Trainingsplan { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @EqualsAndHashCode.Include + private Integer id; + + @Column(nullable = false, unique = true, length = 255) + private String name; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_trainingsziel") + private Trainingsziel trainingsziel; + + @Column(name = "umfang_meter") + private Integer umfangMeter; + + @Column(name = "ist_generiert", nullable = false) + private boolean istGeneriert = false; + + private String detail; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt = Instant.now(); + + @OneToMany(mappedBy = "trainingsplan", + cascade = CascadeType.ALL, + orphanRemoval = true, + fetch = FetchType.LAZY) + @OrderBy("position ASC") + private List bloecke = new ArrayList<>(); +} diff --git a/swim-fit/pom.xml b/swim-fit/pom.xml new file mode 100644 index 0000000..af88f40 --- /dev/null +++ b/swim-fit/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + swim-fit + Swim — FIT (Garmin FIT-File-Erzeugung) + + + + com.triathlon-coaching.product + swim-domain + + + com.triathlon-coaching.product + base-calc + + + + com.garmin + fit-sdk + 21.141.00 + + + diff --git a/swim-pdf/pom.xml b/swim-pdf/pom.xml new file mode 100644 index 0000000..c782ed2 --- /dev/null +++ b/swim-pdf/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + swim-pdf + Swim — PDF (Trainingsplan-PDF-Erzeugung) + + + + com.triathlon-coaching.product + swim-domain + + + com.triathlon-coaching.product + base-calc + + + org.apache.pdfbox + pdfbox + + + diff --git a/swim-web/pom.xml b/swim-web/pom.xml new file mode 100644 index 0000000..27cfcc3 --- /dev/null +++ b/swim-web/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + + com.triathlon-coaching.product + service-parent + 1.0.0-SNAPSHOT + + + swim-web + jar + Swim — Web (Spring Boot App, deployable) + + + + + com.triathlon-coaching.product + swim-domain + + + com.triathlon-coaching.product + swim-pdf + + + com.triathlon-coaching.product + swim-fit + + + + com.triathlon-coaching.product + base-auth + + + com.triathlon-coaching.product + base-user + + + com.triathlon-coaching.product + base-calc + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + + + org.springframework.boot + spring-boot-starter-actuator + + + org.mapstruct + mapstruct + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + diff --git a/swim-web/src/main/java/com/triathlon_coaching/product/swim/web/SwimApplication.java b/swim-web/src/main/java/com/triathlon_coaching/product/swim/web/SwimApplication.java new file mode 100644 index 0000000..a1b36b7 --- /dev/null +++ b/swim-web/src/main/java/com/triathlon_coaching/product/swim/web/SwimApplication.java @@ -0,0 +1,17 @@ +package com.triathlon_coaching.product.swim.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.scheduling.annotation.EnableAsync; + +@SpringBootApplication(scanBasePackages = "com.triathlon_coaching.product") +@EntityScan(basePackages = "com.triathlon_coaching.product") +@EnableJpaRepositories(basePackages = "com.triathlon_coaching.product") +@EnableAsync +public class SwimApplication { + public static void main(String[] args) { + SpringApplication.run(SwimApplication.class, args); + } +} diff --git a/swim-web/src/main/java/com/triathlon_coaching/product/swim/web/controller/SwimController.java b/swim-web/src/main/java/com/triathlon_coaching/product/swim/web/controller/SwimController.java new file mode 100644 index 0000000..d38bda3 --- /dev/null +++ b/swim-web/src/main/java/com/triathlon_coaching/product/swim/web/controller/SwimController.java @@ -0,0 +1,86 @@ +package com.triathlon_coaching.product.swim.web.controller; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +/** + * REST API für swim.triathlon-coaching.com + * + * Öffentlich (kein Login): + * GET /api/swim/plaene → Liste (filterbar, sortierbar) + * GET /api/swim/plaene/{name}/pdf → PDF mit %-Angaben + * GET /api/swim/plaene/{name}/pdf?css=83.5 → PDF mit persönlichen Zeiten + * POST /api/swim/css/berechnen → CSS aus Testzeit + * GET /api/swim/zonen?css=83.5 → alle Zonen mit Zeiten + * + * Auth + Entitlement 'swim_fit' erforderlich: + * GET /api/swim/plaene/{name}/fit → FIT-Download (sichtbar, aber gesperrt ohne Login) + * + * Auth erforderlich: + * GET /api/swim/mein/css → eigene CSS-Historie + * POST /api/swim/mein/css → CSS speichern + */ +@RestController +@RequestMapping("/api/swim") +public class SwimController { + + @GetMapping("/plaene") + public ResponseEntity getPlaene( + @RequestParam(required = false) String ziel, + @RequestParam(required = false) Integer minUmfang, + @RequestParam(required = false) Integer maxUmfang, + @RequestParam(defaultValue = "umfang") String sort, + @RequestParam(defaultValue = "asc") String direction) { + // TODO: TrainingsplanService.findAll(filter, sort) + return ResponseEntity.ok().build(); + } + + @GetMapping("/plaene/{name}/pdf") + public ResponseEntity getPdf( + @PathVariable String name, + @RequestParam(required = false) Double css, + @RequestParam(required = false) Double test400) { + // TODO: TrainingsplanPdfService.erzeugePdf(name, css) + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + name.replace(" ", "_") + ".pdf\"") + .contentType(MediaType.APPLICATION_PDF) + .body(new byte[0]); + } + + /** + * FIT-Download: Endpunkt ist immer erreichbar (für "Lockmittel"-Anzeige), + * aber ohne Entitlement kommt HTTP 402 Payment Required zurück. + */ + @GetMapping("/plaene/{name}/fit") + public ResponseEntity getFit( + @PathVariable String name, + @RequestParam(required = false) Double css) { + // TODO: Security-Check @PreAuthorize("@entitlementService.hasEntitlement('swim_fit')") + // Ohne Login/Entitlement → 402 mit Hinweis auf Registration + return ResponseEntity.status(402).build(); + } + + @PostMapping("/css/berechnen") + public ResponseEntity berechneCss(@RequestBody CssRequest request) { + // TODO: CssCalculator.cssAusTest400() oder cssAusTest200Und400() + return ResponseEntity.ok().build(); + } + + @GetMapping("/zonen") + public ResponseEntity getZonen(@RequestParam double css) { + // TODO: ZonenRechner.berechneAlleZonen(zonenAusDb, css, standardStrecken) + return ResponseEntity.ok().build(); + } + + // ── Records für Request/Response ────────────────────────────────── + + record CssRequest( + Double test400Sek, // 400m-Test in Sekunden + Double t200Sek, // 200m-Test (für präzise CSS-Berechnung) + Double t400Sek, // 400m-Test (für präzise CSS-Berechnung) + Double cssDirectSek // direkte CSS-Eingabe in Sek/100m + ) {} +} diff --git a/swim-web/src/main/resources/application.yml b/swim-web/src/main/resources/application.yml new file mode 100644 index 0000000..e8d4cdf --- /dev/null +++ b/swim-web/src/main/resources/application.yml @@ -0,0 +1,81 @@ +spring: + datasource: + url: jdbc:postgresql://localhost:5432/triathlon_coaching + username: ${DB_USER} + password: ${DB_PASSWORD} + hikari: + maximum-pool-size: 10 + connection-timeout: 30000 + jpa: + hibernate: + ddl-auto: validate # Schema kommt von Flyway, nie von Hibernate + properties: + hibernate: + default_schema: swim + dialect: org.hibernate.dialect.PostgreSQLDialect + format_sql: true + show-sql: false + flyway: + schemas: swim,auth,calc + locations: classpath:db/migration + baseline-on-migrate: true + mail: + host: ${SMTP_HOST} + port: 587 + username: ${SMTP_USER} + password: ${SMTP_PASSWORD} + properties: + mail.smtp.auth: true + mail.smtp.starttls.enable: true + thymeleaf: + cache: false # in Produktion auf true setzen + +server: + port: 8080 + compression: + enabled: true + mime-types: text/html,text/css,application/javascript,application/json + +stc: + auth: + jwt-secret: ${JWT_SECRET} + jwt-expiry-minutes: 30 + session-expiry-days: 30 + base-url: https://swim.triathlon-coaching.com + mail-from: noreply@triathlon-coaching.com + pdf: + watermark: "(c) by triathlon-coaching.com" + logo-path: classpath:static/img/logo-02.png + fit: + enabled: true # false = FIT-Download deaktiviert (z.B. SDK nicht vorhanden) + +management: + endpoints: + web: + exposure: + include: health,info + endpoint: + health: + show-details: when-authorized + +logging: + level: + com.triathlon_coaching.product: INFO + org.springframework.security: WARN + org.flywaydb: INFO + +--- +# Profil: local (Entwicklung) +spring: + config: + activate: + on-profile: local + datasource: + url: jdbc:postgresql://localhost:5432/triathlon_coaching_dev + jpa: + show-sql: true + thymeleaf: + cache: false +logging: + level: + com.triathlon_coaching.product: DEBUG