Skip to content
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
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# QuantCDN Drupal module

This module acts as a static site generator for Drupal and integrates with the [QuantCDN static edge](https://www.quantcdn.io), and is compatible with Drupal 9.x and 10.x.
This module acts as a static site generator for Drupal and integrates with the
[QuantCDN static edge](https://www.quantcdn.io), and is compatible with Drupal
9.x and 10.x.

QuantCDN is a global CDN engineered specifically for the static web and Jamstack.
QuantCDN is a global CDN engineered specifically for the static web and
Jamstack.

It allows one-time compilation and push of an entire Drupal site, as well as tracking and pushes of ongoing content change; the simplest way to export and maintain a static export of your Drupal site.
It allows one-time compilation and push of an entire Drupal site, as well as
tracking and pushes of ongoing content change; the simplest way to export and
maintain a static export of your Drupal site.

Development, issues and feature roadmap occurs in the [GitHub repository](https://github.com/quantcdn/drupal).

Expand Down
39 changes: 39 additions & 0 deletions modules/quant_api/src/Client/QuantClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,45 @@ public function search() {
return FALSE;
}

/**
* {@inheritdoc}
*/
public function purgePath(string $path) : array {

$response = $this->client->post($this->endpoint . '/purge', [
RequestOptions::JSON => [],
'headers' => [
'Quant-Customer' => $this->username,
'Quant-Project' => $this->project,
'Quant-Token' => $this->token,
'Quant-Url' => $path,
],
'verify' => $this->tlsDisabled ? FALSE : TRUE,
]);

return json_decode($response->getBody(), TRUE);
}

/**
* {@inheritdoc}
*/
public function purgeTags(array $tags) : array {

$response = $this->client->post($this->endpoint . '/purge', [
RequestOptions::JSON => [],
'headers' => [
'Quant-Customer' => $this->username,
'Quant-Project' => $this->project,
'Quant-Token' => $this->token,
'Cache-Keys' => implode(' ', $tags),
],
'verify' => $this->tlsDisabled ? FALSE : TRUE,
]);

return json_decode($response->getBody(), TRUE);

}

/**
* {@inheritdoc}
*/
Expand Down
28 changes: 28 additions & 0 deletions modules/quant_api/src/Client/QuantClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,34 @@ public function search();
*/
public function send(array $data) : array;

/**
* Sends a purge payload (path-based) to the API.
*
* @param string $path
* The path to purge.
*
* @return array
* Return array of response data.
*
* @throws \Drupal\quant_api\Exception\InvalidPayload
* @throws \Drupal\quant_api\Exception\InvalidResponse
*/
public function purgePath(string $path) : array;

/**
* Sends a purge payload (tags-based) to the API.
*
* @param array $tags
* The array of tags to purge.
*
* @return array
* Return array of response data.
*
* @throws \Drupal\quant_api\Exception\InvalidPayload
* @throws \Drupal\quant_api\Exception\InvalidResponse
*/
public function purgeTags(array $tags) : array;

/**
* Send a file to the API.
*
Expand Down
30 changes: 27 additions & 3 deletions modules/quant_purger/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
# Quant cache tag purger
# Quant Purger

Adds a cache tag plugin which listens to Drupal invalidation events in order to
queue Quant updates for related content.
The Quant Purger helps you keep content fresh on your static Quant site
after content updates within Drupal.

## Purge Plugins

This module is built on top of the [Purge module suite](https://www.drupal.org/project/purge).

### Purger Plugin

Processes cache invalidations based on type: 'everything', 'path' and 'tag'.
The Quant cache will be purged based on these invalidations.

- *Everything:* A site-wide cache purge, e.g. `/*`.
- *Path:* Purges the given path.
- *Tag:* Purges the given tag.

### Queuer Plugin

Adds a cache tag queuer plugin which listens to Drupal invalidation events in
order to queue Quant updates for related content.

For example, this allows node edits to trigger the main (`/node`) page to update
along with any other pages associated with the node through cache tags (e.g.
Expand All @@ -14,6 +32,12 @@ To ensure that queued content is processed in a timely manner, you can set up a
Quant cron process that is separate from the core cron which just processes the
Quant queue. This Quant cron can be run more regularly than the core cron.

### TagsHeader Plugin

Sets and formats the default response header with hashed cache tags.

## Documentation

See [Quant Purger documentation](https://docs.quantcdn.io/docs/integrations/drupal/purger)
for additional information.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
quant_purger_header:
type: mapping
label: 'Quant Purger Header'
mapping:
field:
type: string
translatable: false
value:
type: string
translatable: false
73 changes: 66 additions & 7 deletions modules/quant_purger/config/schema/quant_purger.schema.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,80 @@
# Schema for the configuration files of the purge_queuer_url module.
quant_purger.settings:
type: config_object
label: 'Quant purger settings.'
label: 'Quant Purger Queuer Settings'
mapping:
tag_blacklist:
label: 'A list of string tags that will not trigger a queue.'
tag_blocklist:
label: 'A list of tags that will not get queued.'
type: sequence
translatable: false
sequence:
type: string
label: 'String that cannot be present in the ccache tag.'
label: 'String that cannot be present in the cache tag.'
translatable: false
path_blacklist:
label: 'A list of string patterns that will not get queued.'
tag_allowlist:
label: 'A list of tags that can get queued.'
type: sequence
translatable: false
sequence:
type: string
label: 'String that can be present in the cache tag.'
translatable: false
path_blocklist:
label: 'A list of paths that will not get queued.'
type: sequence
translatable: false
sequence:
type: string
label: 'String that cannot be present in a fully qualified URL.'
translatable: false
path_allowlist:
label: 'A list of paths that can not get queued.'
type: sequence
translatable: false
sequence:
type: string
label: 'String that can be present in a fully qualified URL.'
translatable: false

quant_purger.settings.*:
type: config_entity
label: 'Quant Purger Settings'
mapping:

#
# Instance metadata:
#
id:
type: string
translatable: false
name:
type: string
translatable: false
invalidation_type:
type: string
translatable: false

#
# Performance settings:
#
runtime_measurement:
type: boolean
translatable: false
timeout:
type: float
translatable: false
connect_timeout:
type: float
translatable: false
cooldown_time:
type: float
translatable: false
max_requests:
type: integer
translatable: false

#
# Success resolution:
#
http_errors:
type: boolean
translatable: false
2 changes: 1 addition & 1 deletion modules/quant_purger/quant_purger.info.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Quant Purger
description: Cache tag purger for Quant.
description: Purge content in Quant based on paths and cache tags.
package: Quant

type: module
Expand Down
2 changes: 1 addition & 1 deletion modules/quant_purger/quant_purger.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ services:
tags:
- { name: http_middleware, priority: 250 }
quant_purger.queuer:
class: Drupal\quant_purger\Plugin\Purge\Queuer\QuantPurger
class: Drupal\quant_purger\Plugin\Purge\Queuer\QuantPurgerQueuerInvalidator
tags:
- { name: cache_tags_invalidator }
calls:
Expand Down
70 changes: 70 additions & 0 deletions modules/quant_purger/src/Entity/Hash.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Drupal\quant_purger\Entity;

/**
* Helper class that centralizes string hashing for security and maintenance.
*/
class Hash {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this make sense as a general utility instead of just in quant_purger?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or not because it's dealing with cache tags?


/**
* Create a hash with the given input and length.
*
* @param string $input
* The input string to be hashed.
* @param int $length
* The length of the hash.
*
* @return string
* Cryptographic hash with the given length.
*/
protected static function hashInput($input, $length) {
// MD5 is the fastest algorithm beyond CRC32 (which is 30% faster, but high
// collision risk), so this is the best bet for now. If collisions are going
// to be a major problem in the future, we might have to consider a hash DB.
$hex = md5($input);
// The produced HEX can be converted to BASE32 number to take less space.
// For example 5 characters HEX can be stored in 4 characters BASE32.
$hash = base_convert(substr($hex, 0, ceil($length * 1.25)), 16, 32);
// Return a hash with consistent length, padding zeroes if needed.
return strtolower(str_pad(substr($hash, 0, $length), $length, '0', STR_PAD_LEFT));
}

/**
* Create unique hashes/IDs for a list of cache tag strings.
*
* @param string[] $tags
* Non-associative array cache tags.
*
* @return string[]
* Non-associative array with hashed copies of the given cache tags.
*/
public static function cacheTags(array $tags) {
$hashes = [];
foreach ($tags as $tag) {
if (strlen($tag) > 4) {
$hashes[] = self::hashInput($tag, 4);
}
else {
$hashes[] = $tag;
}
}
return $hashes;
}

/**
* Create a unique hash that identifies this site.
*
* @param string $site_name
* The identifier of the site on QuantCDN.
* @param string $site_path
* The path of the site, e.g. 'site/default' or 'site/database_a'.
*
* @return string
* Cryptographic hash that's long enough to be unique.
*/
public static function siteIdentifier($site_name, $site_path) {
return self::hashInput($site_name . $site_path, 16);
}

}
Loading