Compare commits

..

4 Commits

4 changed files with 22 additions and 54 deletions

View File

@@ -72,36 +72,18 @@ Application runs on http://localhost:8080
Use a service like ngrok. Use a service like ngrok.
---
## Infisical Webhook Configuration ## Infisical Webhook Configuration
When creating a webhook in Infisical, the following rules must be respected. When creating a webhook in Infisical, the following rules must be respected.
### Webhook URL Formats ### Webhook URL format
Infisical bridge supports two webhook URL formats, depending on the Dokploy resource you want to update.
#### Dokploy Compose Webhook
`${INFISICAL_API_URL}/webhook?dokployComposeId=${DOKPLOY_COMPOSE_ID}` `${INFISICAL_API_URL}/webhook?dokployComposeId=${DOKPLOY_COMPOSE_ID}`
Parameters: - `dokployComposeId` must be the target Dokploy compose identifier
- dokployComposeId (required): - This value is required and used to determine which Dokploy service is updated
The identifier of the target Dokploy Compose.
This value is used to determine which Dokploy compose service should be updated when the webhook is triggered.
#### Dokploy Application Webhook
`${INFISICAL_API_URL}/webhook?dokployApplicationId=${DOKPLOY_APPLICATION_ID}`
Parameters:
- dokployApplicationId (required):
The identifier of the target Dokploy Application.
This value is used to determine which Dokploy application should be updated when the webhook is triggered.
#### Notes
- Exactly one identifier must be provided per webhook URL.
- If no identifier or multiple identifiers are provided, the webhook request will be rejected.
- Ensure the provided ID matches an existing Dokploy resource.
### Webhook Secret ### Webhook Secret
@@ -111,6 +93,8 @@ The webhook secret **must exactly match**:
Requests with an invalid or missing secret will be rejected. Requests with an invalid or missing secret will be rejected.
---
## Webhooks Behavior ## Webhooks Behavior
- Incoming webhook signatures are validated - Incoming webhook signatures are validated
@@ -118,6 +102,8 @@ Requests with an invalid or missing secret will be rejected.
- Dokploy is updated using its API - Dokploy is updated using its API
- Invalid or unsigned requests are ignored - Invalid or unsigned requests are ignored
---
## Security Notes ## Security Notes
- Secrets are never persisted - Secrets are never persisted
@@ -125,12 +111,16 @@ Requests with an invalid or missing secret will be rejected.
- HTTPS is recommended in production - HTTPS is recommended in production
- Restrict network access to trusted sources only - Restrict network access to trusted sources only
---
## Testing ## Testing
```sh ```sh
./gradlew test ./gradlew test
``` ```
---
## Tech Stack ## Tech Stack
- Java 21 - Java 21
@@ -138,6 +128,8 @@ Requests with an invalid or missing secret will be rejected.
- Gradle (Kotlin DSL) - Gradle (Kotlin DSL)
- Docker / Docker Compose - Docker / Docker Compose
---
## License ## License
MIT License MIT License

View File

@@ -4,7 +4,6 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import com.abnov.infisicalbridge.dto.DokployApplicationUpdateRequest;
import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest; import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest;
@FeignClient(name = "dokployClient", url = "${dokploy.api-url}", configuration = DokployFeignConfig.class) @FeignClient(name = "dokployClient", url = "${dokploy.api-url}", configuration = DokployFeignConfig.class)
@@ -12,7 +11,4 @@ public interface DokployClient {
@PostMapping("/compose.update") @PostMapping("/compose.update")
void updateCompose(@RequestBody DokployComposeUpdateRequest request); void updateCompose(@RequestBody DokployComposeUpdateRequest request);
@PostMapping("/application.update")
void updateApplication(@RequestBody DokployApplicationUpdateRequest request);
} }

View File

@@ -1,6 +0,0 @@
package com.abnov.infisicalbridge.dto;
public record DokployApplicationUpdateRequest(
String applicationId,
String env) {
}

View File

@@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.abnov.infisicalbridge.dokploy.DokployClient; import com.abnov.infisicalbridge.dokploy.DokployClient;
import com.abnov.infisicalbridge.dto.DokployApplicationUpdateRequest;
import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest; import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest;
import com.abnov.infisicalbridge.dto.InfisicalWebhookEventResponse; import com.abnov.infisicalbridge.dto.InfisicalWebhookEventResponse;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
@@ -39,8 +38,7 @@ public class InfisicalWebhookController {
@PostMapping @PostMapping
public ResponseEntity<Void> handleWebhook( public ResponseEntity<Void> handleWebhook(
@RequestBody String payload, @RequestBody String payload,
@RequestParam(required = false) String dokployComposeId, @RequestParam String dokployComposeId,
@RequestParam(required = false) String dokployApplicationId,
@RequestHeader(value = "X-Infisical-Signature", required = false) String signature) @RequestHeader(value = "X-Infisical-Signature", required = false) String signature)
throws InfisicalException { throws InfisicalException {
@@ -82,24 +80,12 @@ public class InfisicalWebhookController {
.map(s -> s.getSecretKey() + "=" + s.getSecretValue()) .map(s -> s.getSecretKey() + "=" + s.getSecretValue())
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));
if (dokployComposeId != null) { try {
try { dokployClient.updateCompose(
dokployClient.updateCompose( new DokployComposeUpdateRequest(dokployComposeId, envContent));
new DokployComposeUpdateRequest(dokployComposeId, envContent)); } catch (Exception e) {
} catch (Exception e) { log.error("Failed to update Dokploy compose {}", dokployComposeId, e);
log.error("Failed to update Dokploy compose {}", dokployComposeId, e); return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
}
}
if (dokployApplicationId != null) {
try {
dokployClient.updateApplication(
new DokployApplicationUpdateRequest(dokployApplicationId, envContent));
} catch (Exception e) {
log.error("Failed to update Dokploy application {}", dokployApplicationId, e);
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
}
} }
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();