Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

Multisite Support #70

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
}
],
"require": {
"craftcms/cms": "^3.2.0",
"craftcms/cms": "^3.2.1",
"guzzlehttp/guzzle": "^6.5.5|^7.2.0"
},
"require-dev": {
Expand Down
10 changes: 7 additions & 3 deletions src/EventRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static function registerFrontendEvents()

// Don't cache CP, LivePreview, Action, Non-GET requests
if ($request->getIsCpRequest() ||
$request->getIsLivePreview() ||
$request->getIsPreview() ||
$request->getIsActionRequest() ||
!$request->getIsGet()
) {
Expand Down Expand Up @@ -212,8 +212,12 @@ protected static function handleUpdateEvent(Event $event)
}

// Prevent purge on updates of drafts or revisions
if (ElementHelper::isDraftOrRevision($event->element)) {
return;
try {
if (ElementHelper::isDraftOrRevision($event->element)) {
return;
}
} catch (\Exception $e) {
\Craft::warning("Failed to determine whether element is a Draft or Revision.", "upper");
}

// Prevent purge on resaving
Expand Down
10 changes: 10 additions & 0 deletions src/config.example.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@
'apiToken' => getenv('CLOUDFLARE_API_TOKEN'),
'zoneId' => getenv('CLOUDFLARE_ZONE_ID'),
'domain' => getenv('CLOUDFLARE_DOMAIN'),
'sites' => [
'defaultId' => [
'zoneId' => getenv('CLOUDFLARE_ZONE_ID'),
'domain' => getenv('CLOUDFLARE_DOMAIN'),
],
'alternateId' => [
'zoneId' => getenv('ALT_CLOUDFLARE_ZONE_ID'),
'domain' => getenv('ALT_CLOUDFLARE_DOMAIN'),
],
],
// deprecated, do not use for new installs
'apiKey' => getenv('CLOUDFLARE_API_KEY'),
'apiEmail' => getenv('CLOUDFLARE_API_EMAIL'),
Expand Down
95 changes: 69 additions & 26 deletions src/drivers/Cloudflare.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use ostark\upper\exceptions\CloudflareApiException;
use ostark\upper\Plugin;

/**
* Class Cloudflare Driver
Expand All @@ -29,6 +30,7 @@ class Cloudflare extends AbstractPurger implements CachePurgeInterface

public $domain;

public $sites;

/**
* @param string $tag
Expand All @@ -55,20 +57,48 @@ public function purgeTag(string $tag)
*/
public function purgeUrls(array $urls)
{
if (strpos($this->domain, 'http') !== 0) {
throw new \InvalidArgumentException("'domain' must include the protocol, e.g. https://www.foo.com");
}

// prefix urls with domain
$files = array_map(function($url) {
return rtrim($this->domain, '/') . $url;
}, $urls);

// Chunk larger collections to meet the API constraints
foreach (array_chunk($files, self::MAX_URLS_PER_PURGE) as $fileGroup) {
$this->sendRequest('DELETE', 'purge_cache', [
'files' => $fileGroup
]);
if ($this->sites) {
foreach ($urls as $uid => $url) {
$sql ="SELECT siteId FROM %s WHERE uid = '%s'";
$sql = sprintf(
$sql,
\Craft::$app->getDb()->quoteTableName(Plugin::CACHE_TABLE),
$uid
);
$result = \Craft::$app->getDb()
->createCommand($sql)
->queryOne();
$siteId = $result['siteId'];
if (strpos($this->sites[$siteId]['domain'], 'http') !== 0) {
throw new \InvalidArgumentException("'domain' must include the protocol, e.g. https://www.foo.com");
}
$files[$siteId][] = rtrim($this->sites[$siteId]['domain'], '/') . $url;
}

// Chunk larger collections to meet the API constraints
foreach ($files as $siteId => $siteFiles) {
foreach (array_chunk($siteFiles, self::MAX_URLS_PER_PURGE) as $fileGroup) {
$this->sendRequest('DELETE', 'purge_cache', [
'files' => $fileGroup
], [$this->sites[$siteId]['zoneId']]);
}
}
} else {
if (strpos($this->domain, 'http') !== 0) {
throw new \InvalidArgumentException("'domain' must include the protocol, e.g. https://www.foo.com");
}

// prefix urls with domain
$files = array_map(function($url) {
return rtrim($this->domain, '/') . $url;
}, $urls);

// Chunk larger collections to meet the API constraints
foreach (array_chunk($files, self::MAX_URLS_PER_PURGE) as $fileGroup) {
$this->sendRequest('DELETE', 'purge_cache', [
'files' => $fileGroup
]);
}
}

return true;
Expand All @@ -81,9 +111,16 @@ public function purgeUrls(array $urls)
*/
public function purgeAll()
{
$zoneIds = [];
if ($this->sites) {
foreach ($this->sites as $site) {
$zoneIds[] = $site['zoneId'];
}
}

$success = $this->sendRequest('DELETE', 'purge_cache', [
'purge_everything' => true
]);
'purge_everything' => true,
], $zoneIds);

if ($this->useLocalTags && $success === true) {
$this->clearLocalCache();
Expand All @@ -101,20 +138,26 @@ public function purgeAll()
* @return bool
* @throws \ostark\upper\exceptions\CloudflareApiException
*/
protected function sendRequest($method = 'DELETE', string $type, array $params = [])
protected function sendRequest($method = 'DELETE', string $type, array $params = [], $zoneIds = [])
{
$client = $this->getClient();

try {
$uri = "zones/{$this->zoneId}/$type";
$options = (count($params)) ? ['json' => $params] : [];
$client->request($method, $uri, $options);
} catch (BadResponseException $e) {
if (empty($zoneIds)) {
$zoneIds[] = $this->zoneId;
}

throw CloudflareApiException::create(
$e->getRequest(),
$e->getResponse()
);
foreach ($zoneIds as $zoneId) {
try {
$uri = "zones/$zoneId/$type";
$options = (count($params)) ? ['json' => $params] : [];
$client->request($method, $uri, $options);
} catch (BadResponseException $e) {

throw CloudflareApiException::create(
$e->getRequest(),
$e->getResponse()
);
}
}

return true;
Expand Down