Skip to content

Commit

Permalink
added support for a new custom webhook event 'config.update'
Browse files Browse the repository at this point in the history
  • Loading branch information
albogdano committed Mar 5, 2022
1 parent a675a53 commit 0afbe41
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/main/java/com/erudika/scoold/ScooldConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3076,6 +3076,14 @@ public String rewriteInboundLinksWithFQDN() {
return getConfigParam("rewrite_inbound_links_with_fqdn", "");
}

@Documented(position = 3010,
identifier = "cluster_nodes",
category = "Miscellaneous",
description = "Total number of nodes present in the cluster when Scoold is deployed behind a reverse proxy.")
public int clusterNodes() {
return getConfigInt("cluster_nodes", 1);
}

/* **********************************************************************************************************/

public boolean inDevelopment() {
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/erudika/scoold/api/ApiController.java
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,7 @@ public String configSet(HttpServletRequest req, HttpServletResponse res) {
ConfigFactory.invalidateCaches();
CONF.store();
pc.setAppSettings(CONF.getParaAppSettings());
triggerConfigUpdateEvent(CONF.getConfigMap());
return config(req, res);
}

Expand All @@ -945,6 +946,7 @@ public void configSet(@PathVariable String key, HttpServletRequest req, HttpServ
if (CONF.getParaAppSettings().containsKey(key)) {
pc.addAppSetting(key, value);
}
triggerConfigUpdateEvent(Collections.singletonMap(CONF.getConfigRootPrefix() + "." + key, value));
}
}

Expand Down Expand Up @@ -987,6 +989,28 @@ private List<String> readSpaces(String... spaces) {
return readSpaces(Arrays.asList(spaces));
}

private void triggerConfigUpdateEvent(Map<String, Object> payload) {
int nodes = CONF.clusterNodes();
if (nodes > 1) {
Para.asyncExecute(() -> {
Webhook trigger = new Webhook();
trigger.setId(Utils.getNewId());
trigger.setUpdate(true);
trigger.setUpdateAll(true);
trigger.setSecret("{{secretKey}}");
trigger.setActive(true);
trigger.setUrlEncoded(false);
trigger.setTargetUrl(CONF.serverUrl() + "/webhooks/config");
trigger.setTriggeredEvent("config.update");
trigger.setCustomPayload(payload);
// the goal is to saturate the load balancer and hopefully the payload reaches all nodes behind it
trigger.setRepeatedDeliveryAttempts(nodes * 2);
Para.getCache().put("lastConfigUpdate", trigger.getId());
pc.create(trigger);
});
}
}

@ExceptionHandler({Exception.class})
public Map<String, Object> handleException(Exception ex, WebRequest request, HttpServletResponse res) {
Map<String, Object> error = new HashMap<>(2);
Expand Down
86 changes: 86 additions & 0 deletions src/main/java/com/erudika/scoold/api/WebhooksController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2013-2022 Erudika. https://erudika.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For issues and patches go to: https://github.com/erudika
*/
package com.erudika.scoold.api;

import com.erudika.para.client.ParaClient;
import com.erudika.para.core.utils.Config;
import com.erudika.para.core.utils.Para;
import com.erudika.para.core.utils.ParaObjectUtils;
import com.erudika.para.core.utils.Utils;
import com.erudika.scoold.ScooldConfig;
import static com.erudika.scoold.api.ApiController.logger;
import com.erudika.scoold.utils.ScooldUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.typesafe.config.ConfigFactory;
import java.util.Collections;
import java.util.Map;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* Handles various webhook events.
* @author Alex Bogdanovski [[email protected]]
*/
@RestController
@RequestMapping(value = "/webhooks", produces = "application/json")
public class WebhooksController {

private final ScooldUtils utils;
private final ParaClient pc;
private static final ScooldConfig CONF = ScooldUtils.getConfig();

@Inject
public WebhooksController(ScooldUtils utils) {
this.utils = utils;
this.pc = utils.getParaClient();
}

@PostMapping("/config")
public void updateConfig(HttpServletRequest req, HttpServletResponse res) throws JsonProcessingException {
Map<String, Object> entity = readEntity(req);
if (entity.containsKey("signature") && entity.containsKey("payload") &&
entity.getOrDefault("event", "").equals("config.update")) {
String payload = (String) entity.get("payload");
String signature = (String) entity.get("signature");
String id = (String) entity.get(Config._ID);
boolean alreadyUpdated = id.equals(Para.getCache().get("lastConfigUpdate"));
if (StringUtils.equals(signature, Utils.hmacSHA256(payload, CONF.paraSecretKey())) && !alreadyUpdated) {
Map<String, Object> configMap = ParaObjectUtils.getJsonReader(Map.class).readValue(payload);
configMap.entrySet().forEach((entry) -> {
System.setProperty(entry.getKey(), entry.getValue().toString());
});
ConfigFactory.invalidateCaches();
CONF.store();
}
}
}

private Map<String, Object> readEntity(HttpServletRequest req) {
try {
return ParaObjectUtils.getJsonReader(Map.class).readValue(req.getInputStream());
} catch (Exception ex) {
logger.error(null, ex);
}
return Collections.emptyMap();
}
}

0 comments on commit 0afbe41

Please sign in to comment.