In order to clear Cloudflare cache automatically via code, follow below steps:
Sample code is here:
Create a custom service user to control permission what should be allowed to flush CDN cache: cloudflare-flush
Reference: https://api.cloudflare.com/#zone-purge-files-by-url/
1. Develop Custom TransportHandler
Develop a custom Transport handler to send the test and purge requests to Cloudflare API server. The handler sets up basic authentication with the user/pass from the replication agent's transport config and sends a GET request as a test and POST as purge request. A valid test response is 200 while a valid purge response is 201.Sample code is here:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.vivek.sample.handler.impl; | |
import java.io.IOException; | |
import java.util.HashMap; | |
import org.apache.commons.codec.CharEncoding; | |
import org.apache.commons.lang3.StringUtils; | |
import org.apache.http.HttpHeaders; | |
import org.apache.http.HttpResponse; | |
import org.apache.http.HttpStatus; | |
import org.apache.http.client.HttpClient; | |
import org.apache.http.client.config.RequestConfig; | |
import org.apache.http.client.methods.HttpGet; | |
import org.apache.http.client.methods.HttpPost; | |
import org.apache.http.client.methods.HttpRequestBase; | |
import org.apache.http.entity.ContentType; | |
import org.apache.http.entity.StringEntity; | |
import org.apache.http.impl.client.HttpClientBuilder; | |
import org.apache.sling.api.resource.LoginException; | |
import org.apache.sling.api.resource.ResourceResolver; | |
import org.apache.sling.api.resource.ResourceResolverFactory; | |
import org.osgi.service.component.annotations.Component; | |
import org.osgi.service.component.annotations.Reference; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import com.day.cq.commons.Externalizer; | |
import com.day.cq.replication.AgentConfig; | |
import com.day.cq.replication.ReplicationActionType; | |
import com.day.cq.replication.ReplicationResult; | |
import com.day.cq.replication.ReplicationTransaction; | |
import com.day.cq.replication.TransportContext; | |
import com.day.cq.replication.TransportHandler; | |
import com.google.gson.JsonArray; | |
import com.google.gson.JsonObject; | |
/** | |
* Transport handler to send test and purge requests to Cloudflare and handle | |
* responses. The handler sets up basic authentication with the user/pass from | |
* the replication agent's transport config and sends a GET request as a test | |
* and POST as purge request. A valid test response is 200 while a valid purge | |
* response is 201. | |
* | |
* The Flush agent must be configured with following 3 properties: | |
* | |
* 1. The transport handler is triggered by setting your replication agent's | |
* transport URL's protocol to "cloudflare://". E.g. - | |
* cloudflare://api.cloudflare.com/client/v4/zones/{zone-id}/purge_cache | |
* | |
* 2. User: The X-Auth-Email value of cloudflare account | |
* | |
* 3. Password: X-Auth-Key value of cloudflare account | |
* | |
* The transport handler builds the POST request body in accordance with | |
* Cloudflare's REST APIs | |
* {@link https://api.cloudflare.com/#zone-purge-files-by-url/} using the | |
* replication agent properties. | |
*/ | |
@Component(service = TransportHandler.class, name = "Cloudflare-Purge-Agent", immediate = true) | |
public class CloudflareTransportHandler implements TransportHandler { | |
/** | |
* externalizer | |
*/ | |
@Reference | |
private Externalizer externalizer; | |
/** | |
* resolverFactory | |
*/ | |
@Reference | |
private ResourceResolverFactory resolverFactory; | |
/** | |
* Protocol for replication agent transport URI that triggers this transport | |
* handler. | |
*/ | |
private static final String CF_PROTOCOL = "cloudflare://"; | |
/** | |
* Key parameter name | |
*/ | |
private static final String CF_PARAM_KEY = "X-Auth-Key"; | |
/** | |
* email parameter name | |
*/ | |
private static final String CF_PARAM_EMAIL = "X-Auth-Email"; | |
/** | |
* files parameter name | |
*/ | |
private static final String CF_PARAM_FILES = "files"; | |
/** | |
* CF test endpoint | |
*/ | |
private static final String CF_TEST_ENDPOINT = "https://%s/client/v4/zones"; | |
/** | |
* Timeout for http requests | |
*/ | |
private static final int HTTP_TIMEOUT = 15; | |
/** | |
* Logger | |
*/ | |
private static final Logger LOGGER = LoggerFactory.getLogger(CloudflareTransportHandler.class); | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public final boolean canHandle(final AgentConfig config) { | |
final String transportURI = config.getTransportURI(); | |
if (transportURI != null) { | |
return transportURI.toLowerCase().startsWith(CF_PROTOCOL); | |
} else { | |
return false; | |
} | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public final ReplicationResult deliver(final TransportContext ctx, final ReplicationTransaction tx) { | |
final ReplicationActionType replicationType = tx.getAction().getType(); | |
if (replicationType == ReplicationActionType.TEST) { | |
return doTest(ctx, tx); | |
} else if (replicationType == ReplicationActionType.ACTIVATE | |
|| replicationType == ReplicationActionType.DEACTIVATE) { | |
return doActivate(ctx, tx); | |
} | |
return ReplicationResult.OK; | |
} | |
/** | |
* Send test request to Cloudflare via a GET request. | |
* | |
* Cloudflare will respond with a 200 HTTP status code if the request was | |
* successfully submitted. The response will have information about the queue | |
* length, but we're simply interested in the fact that the request was | |
* authenticated. | |
* | |
* @param ctx Transport Context | |
* @param tx Replication Transaction | |
* @return ReplicationResult OK if 200 response from Cloudflare | |
*/ | |
private ReplicationResult doTest(final TransportContext ctx, final ReplicationTransaction tx) { | |
String uri = ctx.getConfig().getTransportURI().replace(CF_PROTOCOL, ""); | |
String domain = uri.substring(0, uri.indexOf("/")); | |
final HttpGet request = new HttpGet(String.format(CF_TEST_ENDPOINT, domain)); | |
return getResult(sendRequest(request, ctx, tx), tx); | |
} | |
/** | |
* Send purge request to Cloudflare via a POST request | |
* | |
* Cloudflare will respond with a 201 HTTP status code if the purge request was | |
* successfully submitted. | |
* | |
* @param ctx - Transport Context | |
* @param tx - Replication Transaction | |
* @return ReplicationResult - OK if 201 response from Cloudflare | |
*/ | |
private ReplicationResult doActivate(final TransportContext ctx, final ReplicationTransaction tx) { | |
final String cfEndPoint = ctx.getConfig().getTransportURI().replace(CF_PROTOCOL, "https://"); | |
final HttpPost request = new HttpPost(cfEndPoint); | |
createPostBody(request, tx); | |
return getResult(sendRequest(request, ctx, tx), tx); | |
} | |
/** | |
* Get Replication Result | |
* | |
* @param response - HttpResponse object | |
* @param tx - ReplicationTransaction object | |
* @return ReplicationResult - result | |
*/ | |
private ReplicationResult getResult(final HttpResponse response, final ReplicationTransaction tx) { | |
if (response != null) { | |
final int statusCode = response.getStatusLine().getStatusCode(); | |
tx.getLog().info(response.toString()); | |
tx.getLog().info("---------------------------------------"); | |
if (statusCode == HttpStatus.SC_OK) { | |
return ReplicationResult.OK; | |
} | |
} | |
return new ReplicationResult(false, 0, "Replication failed"); | |
} | |
/** | |
* Build preemptive basic authentication headers and send request. | |
* | |
* @param <T> Type | |
* @param request - The request to send to Cloudflare | |
* @param ctx - The TransportContext containing the username and password | |
* @param tx - Replication Transaction | |
* @return HttpResponse - The HTTP response from Cloudflare | |
*/ | |
private <T extends HttpRequestBase> HttpResponse sendRequest(final T request, final TransportContext ctx, | |
final ReplicationTransaction tx) { | |
request.setHeader(CF_PARAM_KEY, ctx.getConfig().getTransportPassword()); | |
request.setHeader(CF_PARAM_EMAIL, ctx.getConfig().getTransportUser()); | |
request.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType()); | |
RequestConfig config = RequestConfig.custom().setConnectTimeout(HTTP_TIMEOUT * 1000) | |
.setConnectionRequestTimeout(HTTP_TIMEOUT * 1000) | |
.setSocketTimeout(HTTP_TIMEOUT * 1000).build(); | |
HttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); | |
HttpResponse response = null; | |
try { | |
response = client.execute(request); | |
} catch (IOException e) { | |
tx.getLog().error("Could not send replication request- " + e.getMessage()); | |
} | |
return response; | |
} | |
/** | |
* Build the Cloudflare purge request body based on the replication agent | |
* settings and append it to the POST request. | |
* | |
* @param request The HTTP POST request to append the request body | |
* @param tx ReplicationTransaction | |
*/ | |
private void createPostBody(final HttpPost request, final ReplicationTransaction tx) { | |
JsonObject json = new JsonObject(); | |
JsonArray purgeObjects = new JsonArray(); | |
for (String path : tx.getAction().getPaths()) { | |
if (StringUtils.isNotBlank(path) && path.startsWith("/content/")) { | |
purgeObjects.add(externalizer.externalLink(getServiceResourceResolver(resolverFactory, "read-service"), | |
Externalizer.PUBLISH, path)); | |
} | |
} | |
if (purgeObjects.size() > 0) { | |
json.add(CF_PARAM_FILES, purgeObjects); | |
final StringEntity entity = new StringEntity(json.toString(), CharEncoding.ISO_8859_1); | |
tx.getLog().info("Clearing cache for paths param: " + json); | |
request.setEntity(entity); | |
} | |
} | |
/** | |
* Get Resolver | |
* | |
* @param resolverFactory - resolver factory | |
* @param subservice - sub-service | |
* @return ResourceResolver resolver | |
*/ | |
private static ResourceResolver getServiceResourceResolver(final ResourceResolverFactory resolverFactory, | |
final String subservice) { | |
HashMap<String, Object> param = new HashMap<>(); | |
param.put(ResourceResolverFactory.SUBSERVICE, subservice); | |
try { | |
return resolverFactory.getServiceResourceResolver(param); | |
} catch (LoginException e) { | |
LOGGER.error("Login Excpetion in getting service resource resolver. Error: {}", e.getMessage(), e); | |
} | |
return null; | |
} | |
} | |
2. Create custom dispatcher flush agent
The transport handler builds the POST request body in accordance with Cloudflare's REST APIs using the replication agent properties.A. Create New Replication Agent at-
http://localhost:4503/miscadmin#/etc/replication/agents.publishB. Agent Configuration:
The Flush agent must be configured with following 3 properties:
1. The transport handler is triggered by setting agent's transport URL's protocol to "cloudflare://".
E.g. - cloudflare://api.cloudflare.com/client/v4/zones/{zone-id}/purge_cache
2. User: The X-Auth-Email value of cloudflare account
3. Password: X-Auth-Key value of cloudflare account
1. The transport handler is triggered by setting agent's transport URL's protocol to "cloudflare://".
E.g. - cloudflare://api.cloudflare.com/client/v4/zones/{zone-id}/purge_cache
2. User: The X-Auth-Email value of cloudflare account
3. Password: X-Auth-Key value of cloudflare account
C. Agent User Id in AEM:
Create a custom service user to control permission what should be allowed to flush CDN cache: cloudflare-flush
Reference: https://api.cloudflare.com/#zone-purge-files-by-url/
No comments:
Post a Comment