diff --git a/docker-compose.yml b/docker-compose.yml index d5fc59f..f716aad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,26 +4,9 @@ services: restart: unless-stopped ports: - "8080:8080" - volumes: - # Mount your custom config here — overrides the defaults inside the image - - ./config:/app/config:ro - environment: - # Optional: override the port - - LOG_LEVEL=debug - - SERVER_PORT=8080 networks: - seerr-net - # ── Jellyseerr (local test instance) ───────────────────────────────────── - # Access the UI at: http://localhost:5055 - # - # After first start, go through the setup wizard. When asked for a media - # server you can skip it or point to a local Jellyfin (see below). - # - # Webhook setup in Jellyseerr: - # Settings → Notifications → Webhook - # URL: http://webhook-proxy:8080/webhook ← uses the internal Docker hostname - # Payload: leave as default seerr: image: seerr/seerr:latest container_name: seerr @@ -36,9 +19,6 @@ services: networks: - seerr-net - # ── Jellyfin (optional — only needed if Jellyseerr asks for a media server) - # Access the UI at: http://localhost:8096 - # Remove this block if you don't want a full media server locally. jellyfin: image: jellyfin/jellyfin:latest container_name: jellyfin diff --git a/src/main/java/dev/webhookproxy/config/ProxyConfig.java b/src/main/java/dev/webhookproxy/config/ProxyConfig.java index 2173d02..a1356e9 100644 --- a/src/main/java/dev/webhookproxy/config/ProxyConfig.java +++ b/src/main/java/dev/webhookproxy/config/ProxyConfig.java @@ -4,19 +4,13 @@ import lombok.*; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.List; -import java.util.Map; @Data @Component @ConfigurationProperties(prefix = "proxy") public class ProxyConfig { - /** - * Map of notification_type → list of Discord webhook URLs. - * Use the key "default" to catch all unmatched notification types. - */ private List routes = List.of(); private Filters filters; diff --git a/src/main/java/dev/webhookproxy/controller/WebhookController.java b/src/main/java/dev/webhookproxy/controller/WebhookController.java index 6633902..8beef2b 100644 --- a/src/main/java/dev/webhookproxy/controller/WebhookController.java +++ b/src/main/java/dev/webhookproxy/controller/WebhookController.java @@ -9,12 +9,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -/** - * Receives incoming Overseerr webhook POST requests and - * delegates forwarding to {@link DiscordForwardingService}. - * - * Endpoint: POST /webhook - */ @Slf4j @RestController @RequiredArgsConstructor @@ -24,10 +18,6 @@ public class WebhookController { private final ObjectMapper objectMapper; private final ProxyConfig proxyConfig; - /** - * Main webhook endpoint. - * Overseerr should be configured to POST to: http://:/webhook - */ @PostMapping("/webhook") public ResponseEntity receiveWebhook(@RequestBody String rawBody) { log.info("Received webhook payload: {}", rawBody); @@ -51,17 +41,8 @@ public class WebhookController { } } - // Forward asynchronously — we return 200 immediately to Overseerr forwardingService.forward(payload, rawBody); return ResponseEntity.ok("OK"); } - - /** - * Simple health / test endpoint. - */ - @GetMapping("/health") - public ResponseEntity health() { - return ResponseEntity.ok("Webhook proxy is running."); - } } diff --git a/src/main/java/dev/webhookproxy/model/SeerrPayload.java b/src/main/java/dev/webhookproxy/model/SeerrPayload.java index 2b69c24..0428daf 100644 --- a/src/main/java/dev/webhookproxy/model/SeerrPayload.java +++ b/src/main/java/dev/webhookproxy/model/SeerrPayload.java @@ -23,10 +23,7 @@ public class SeerrPayload { /** Media overview / synopsis, or issue description. */ private String message; - - /** URL to the media poster image. */ private String image; - private Media media; private Request request; private Issue issue; @@ -39,14 +36,9 @@ public class SeerrPayload { @JsonProperty("media_type") private String mediaType; - private String tmdbId; private String tvdbId; - - /** Availability status: UNKNOWN | PENDING | PROCESSING | PARTIALLY_AVAILABLE | AVAILABLE */ private String status; - - /** 4K availability status (same values as status). */ private String status4k; } @@ -80,11 +72,9 @@ public class SeerrPayload { @JsonProperty("issue_id") private String issueId; - /** VIDEO | AUDIO | SUBTITLES | OTHER */ @JsonProperty("issue_type") private String issueType; - /** OPEN | IN_PROGRESS | RESOLVED */ @JsonProperty("issue_status") private String issueStatus; diff --git a/src/main/java/dev/webhookproxy/service/DiscordForwardingService.java b/src/main/java/dev/webhookproxy/service/DiscordForwardingService.java index 74e710d..63e5b1e 100644 --- a/src/main/java/dev/webhookproxy/service/DiscordForwardingService.java +++ b/src/main/java/dev/webhookproxy/service/DiscordForwardingService.java @@ -18,10 +18,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; -/** - * Builds a Discord embed message from the Overseerr payload - * and forwards it concurrently to all configured webhook URLs. - */ @Slf4j @Service @RequiredArgsConstructor @@ -34,12 +30,6 @@ public class DiscordForwardingService { .connectTimeout(Duration.ofSeconds(5)) .build(); - /** - * Forwards the payload to all Discord webhooks matching the notification type. - * - * @param payload the parsed Overseerr payload - * @param rawBody the original raw JSON body (used as fallback) - */ public void forward(SeerrPayload payload, String rawBody) { String mediaName = payload.getSubject(); List targets = proxyConfig.getRoutes(); @@ -53,20 +43,14 @@ public class DiscordForwardingService { String discordBody = buildDiscordPayload(payload); - // Fire all requests concurrently List> futures = new ArrayList<>(); for (String webhookUrl : targets) { futures.add(sendAsync(webhookUrl, discordBody, mediaName)); } - // Wait for all to complete (best-effort, errors are logged but not re-thrown) CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); } - // ------------------------------------------------------------------------- - // Internal helpers - // ------------------------------------------------------------------------- - private CompletableFuture sendAsync(String webhookUrl, String body, String notificationType) { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(webhookUrl)) @@ -92,10 +76,6 @@ public class DiscordForwardingService { }); } - /** - * Builds a Discord-compatible JSON payload with an embed. - * Discord webhooks expect: { "embeds": [ { "title": "...", ... } ] } - */ private String buildDiscordPayload(SeerrPayload payload) { try { int color = colorForType(payload.getNotificationType()); @@ -110,7 +90,6 @@ public class DiscordForwardingService { embed.put("thumbnail", Map.of("url", payload.getImage())); } - // Field: requested by List> fields = new ArrayList<>(); if (payload.getRequest() != null && payload.getRequest().getRequestedByUsername() != null) { fields.add(field("Requested by", payload.getRequest().getRequestedByUsername(), true)); @@ -153,9 +132,6 @@ public class DiscordForwardingService { return Map.of("name", name, "value", value, "inline", inline); } - /** - * Returns a Discord embed color (decimal) based on the notification type. - */ private int colorForType(String type) { if (type == null) return 0x95A5A6; // grey return switch (type) { @@ -166,11 +142,6 @@ public class DiscordForwardingService { }; } - /** - * Masks the webhook token in the URL for safe logging. - * Input: https://discord.com/api/webhooks/1234567890/AbCdEfGhIj... - * Output: https://discord.com/api/webhooks/1234567890/Ab*** - */ private String maskUrl(String url) { int lastSlash = url.lastIndexOf('/'); if (lastSlash < 0 || lastSlash >= url.length() - 1) return url;