feat: ✨ add RSS and Atom feeds with caching and nice links
This commit is contained in:
parent
c6297fd81b
commit
596cc0e1a3
7 changed files with 120 additions and 1 deletions
|
@ -40,6 +40,17 @@ CREATE TABLE path_access_counts (
|
||||||
PRIMARY KEY(access_time, post_path, agent, referrer)
|
PRIMARY KEY(access_time, post_path, agent, referrer)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE feed_cache (
|
||||||
|
search_path VARCHAR(255),
|
||||||
|
export_type VARCHAR(255),
|
||||||
|
|
||||||
|
feed_created_on DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
feed_content MEDIUMTEXT,
|
||||||
|
|
||||||
|
PRIMARY KEY(search_path, export_type)
|
||||||
|
);
|
||||||
|
|
||||||
INSERT INTO posts (post_path, post_path_depth, post_metadata, post_content)
|
INSERT INTO posts (post_path, post_path_depth, post_metadata, post_content)
|
||||||
VALUES (
|
VALUES (
|
||||||
'/about',
|
'/about',
|
||||||
|
|
|
@ -44,7 +44,7 @@ class MySQLAdapter {
|
||||||
return $post_path;
|
return $post_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _exec($qery, $argtypes, ...$args) {
|
function _exec($qery, $argtypes = '', ...$args) {
|
||||||
$stmt = $this->raw->prepare($qery);
|
$stmt = $this->raw->prepare($qery);
|
||||||
if($argtypes != ""){
|
if($argtypes != ""){
|
||||||
$stmt->bind_param($argtypes, ...$args);
|
$stmt->bind_param($argtypes, ...$args);
|
||||||
|
|
|
@ -57,6 +57,11 @@ class PostHandler extends MySQLAdapter {
|
||||||
parent::make_post_directory($directory);
|
parent::make_post_directory($directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update_or_create_post(...$args) {
|
||||||
|
$this->_exec("TRUNCATE feed_cache");
|
||||||
|
parent::update_or_create_post(...$args);
|
||||||
|
}
|
||||||
|
|
||||||
function save_file($post_path, $file_path) {
|
function save_file($post_path, $file_path) {
|
||||||
$this->bump_post($post_path);
|
$this->bump_post($post_path);
|
||||||
move_uploaded_file($file_path, $this->data_directory . $post_path);
|
move_uploaded_file($file_path, $this->data_directory . $post_path);
|
||||||
|
@ -101,6 +106,86 @@ class PostHandler extends MySQLAdapter {
|
||||||
$this->save_file($post_path, $file_path);
|
$this->save_file($post_path, $file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function try_get_cached_feed($path, $export_opt) {
|
||||||
|
$post_cache = $this->_exec("SELECT feed_content, feed_created_on
|
||||||
|
FROM feed_cache
|
||||||
|
WHERE search_path=? AND export_type=?", "ss", $path, $export_opt)->fetch_assoc();
|
||||||
|
|
||||||
|
if(!isset($post_cache)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['feed' => $post_cache['feed_content'], 'feed_ts' => $post_cache['feed_created_on']];
|
||||||
|
}
|
||||||
|
|
||||||
|
function construct_feed($path) {
|
||||||
|
$path = $this->_sanitize_path($path);
|
||||||
|
|
||||||
|
$feed = @new Feed;
|
||||||
|
|
||||||
|
$feed->setTitle("DergFeed");
|
||||||
|
$feed->setLink("https://lucidragons.de" . $path);
|
||||||
|
$feed->setFeedLink("https://lucidragons.de/feeds/atom" . $path, "atom");
|
||||||
|
|
||||||
|
$feed->setDateModified(time());
|
||||||
|
|
||||||
|
$feed->setDescription("DergenFeed for all your " . $path . " needs <3");
|
||||||
|
|
||||||
|
$feed_posts = $this->_exec("SELECT
|
||||||
|
post_path,
|
||||||
|
post_create_time, post_update_time,
|
||||||
|
post_content,
|
||||||
|
post_metadata
|
||||||
|
FROM posts
|
||||||
|
WHERE (post_path = ?) OR (post_path LIKE ?)
|
||||||
|
ORDER BY post_create_time DESC LIMIT 200",
|
||||||
|
"ss", $path, $path . '/%');
|
||||||
|
|
||||||
|
while($row = $feed_posts->fetch_array(MYSQLI_ASSOC)) {
|
||||||
|
$row = $this->_normalize_post_data($row);
|
||||||
|
$pmeta = $row['post_metadata'];
|
||||||
|
|
||||||
|
if($pmeta['type'] == 'directory') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = $feed->createEntry();
|
||||||
|
|
||||||
|
$entry->setTitle($row['post_path'] . '> ' . $pmeta['title']);
|
||||||
|
$entry->setLink('https://lucidragons.de' . $row['post_path']);
|
||||||
|
$entry->setDateModified(strtotime($row['post_update_time']));
|
||||||
|
$entry->setDateCreated(strtotime($row['post_create_time']));
|
||||||
|
|
||||||
|
$entry->setDescription($pmeta['brief'] ?? $pmeta['title']);
|
||||||
|
|
||||||
|
$feed->addEntry($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_laminas_feed($path, $export_opt) {
|
||||||
|
$path = $this->_sanitize_path($path);
|
||||||
|
|
||||||
|
$feed_cache = $this->try_get_cached_feed($path, $export_opt);
|
||||||
|
|
||||||
|
if(isset($feed_cache)) {
|
||||||
|
return $feed_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
$feed = $this->construct_feed($path);
|
||||||
|
|
||||||
|
$this->_exec("INSERT INTO feed_cache
|
||||||
|
(search_path, export_type, feed_content)
|
||||||
|
VALUES
|
||||||
|
(?, 'atom', ?),
|
||||||
|
(?, 'rss', ?)",
|
||||||
|
"ssss", $path, $feed->export('atom'),
|
||||||
|
$path, $feed->export('rss'));
|
||||||
|
|
||||||
|
return $this->try_get_cached_feed($path, $export_opt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -111,6 +111,14 @@ function generate_website($SURI) {
|
||||||
|
|
||||||
echo $twig->render('upload.html');
|
echo $twig->render('upload.html');
|
||||||
}
|
}
|
||||||
|
} elseif(preg_match('/^\/feed(?:\/(rss|atom)(.*))?$/', $SURI, $match)) {
|
||||||
|
$feed = $adapter->get_laminas_feed($match[2] ?? '/', $match[1] ?? 'rss');
|
||||||
|
|
||||||
|
header('Content-Type: application/xml');
|
||||||
|
header('Cache-Control: max-age=1800');
|
||||||
|
header('Etag: W/"' . $SURI . '/' . strtotime($feed['feed_ts']) . '"');
|
||||||
|
|
||||||
|
echo $feed['feed'];
|
||||||
} elseif(preg_match('/^\s*image/', $_SERVER['HTTP_ACCEPT'])) {
|
} elseif(preg_match('/^\s*image/', $_SERVER['HTTP_ACCEPT'])) {
|
||||||
header('Location: /raw' . $SURI);
|
header('Location: /raw' . $SURI);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
|
@ -26,5 +26,11 @@
|
||||||
<a href="/raw{{post.post_path}}">raw</a>
|
<a href="/raw{{post.post_path}}">raw</a>
|
||||||
<a href="/api/posts{{post.post_path}}">api</a>
|
<a href="/api/posts{{post.post_path}}">api</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a rel="alternate" type="application/rss+xml" target="_blank"
|
||||||
|
style="padding-left: 0.3rem;" href="/feed/rss{{post.post_path}}">
|
||||||
|
{{ fa['rss']|raw }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</menu>
|
</menu>
|
||||||
</div>
|
</div>
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
{% extends "root.html" %}
|
{% extends "root.html" %}
|
||||||
|
|
||||||
|
{% block feed_links %}
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="DergSite Global Feed" href="https://lucidragons.de/feed">
|
||||||
|
<link rel="alternate" type="application/atom+xml" title="DergSite Feed for {{post.post_path}}" href="https://lucidragons.de/feed/atom{{post.post_path}}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block second_title %}
|
{% block second_title %}
|
||||||
<h2> {{ post.post_metadata.title }} </h2>
|
<h2> {{ post.post_metadata.title }} </h2>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
|
||||||
|
{% block feed_links %}
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="DergSite Global Feed" href="https://lucidragons.de/feed">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_head %}{% endblock %}
|
{% block extra_head %}{% endblock %}
|
||||||
|
|
||||||
<script src="/static/banner.js" defer></script>
|
<script src="/static/banner.js" defer></script>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue