WebsiteTemplate/blog/api/feed.php
2026-01-25 11:33:37 -04:00

128 lines
4.0 KiB
PHP

<?php
// Timezone: where RSS feeds go to die and be reborn
date_default_timezone_set('UTC'); // Adjust to your server's actual timezone
// RSS Feed with analytics tracking
header('Content-Type: application/xml; charset=utf-8');
// Track RSS feed access and active subscribers
$dataDir = '/var/www/data/analytics';
$date = date('Y-m-d');
$hour = date('H');
$summaryFile = $dataDir . '/summary_' . $date . '.json';
$readersFile = $dataDir . '/rss_readers.json';
// Track unique RSS readers
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$ip = $_SERVER['REMOTE_ADDR'] ?? 'Unknown';
$readerId = md5($userAgent . $ip); // Unique identifier for this reader
$now = time();
// Load existing readers data
$readers = [];
if (file_exists($readersFile)) {
$readers = json_decode(file_get_contents($readersFile), true) ?: [];
}
// Update or add this reader's last fetch time
$readers[$readerId] = [
'lastFetch' => $now,
'userAgent' => substr($userAgent, 0, 200), // Limit length
'ip' => $ip,
'firstSeen' => $readers[$readerId]['firstSeen'] ?? $now
];
// Clean up old readers (haven't fetched in 30+ days to keep file size manageable)
$thirtyDaysAgo = $now - (30 * 24 * 60 * 60);
foreach ($readers as $id => $reader) {
if ($reader['lastFetch'] < $thirtyDaysAgo) {
unset($readers[$id]);
}
}
// Save readers data
file_put_contents($readersFile, json_encode($readers, JSON_PRETTY_PRINT));
// Calculate active subscribers (fetched in last 7 days)
$sevenDaysAgo = $now - (7 * 24 * 60 * 60);
$activeSubscribers = 0;
foreach ($readers as $reader) {
if ($reader['lastFetch'] >= $sevenDaysAgo) {
$activeSubscribers++;
}
}
// Update summary - track RSS feed fetch (for historical data)
$summary = [];
if (file_exists($summaryFile)) {
$summary = json_decode(file_get_contents($summaryFile), true) ?: [];
}
if (!isset($summary['rss'])) {
$summary['rss'] = 0;
}
$summary['rss']++;
$summary['activeRssSubscribers'] = $activeSubscribers; // Store active count
// Ensure directory is writable
$result = file_put_contents($summaryFile, json_encode($summary, JSON_PRETTY_PRINT));
if ($result === false) {
error_log("Failed to write RSS tracking to $summaryFile");
}
// Generate and output RSS feed
$postsFile = __DIR__ . '/../data/posts.json';
$posts = [];
if (file_exists($postsFile)) {
$posts = json_decode(file_get_contents($postsFile), true) ?: [];
}
// Sort posts by date (newest first)
usort($posts, function($a, $b) {
return strtotime($b['date']) - strtotime($a['date']);
});
// Escape XML
function escapeXml($str) {
if (!$str) return '';
return htmlspecialchars($str, ENT_XML1, 'UTF-8');
}
// Format content
function formatContent($content) {
return nl2br(htmlspecialchars($content, ENT_XML1, 'UTF-8'));
}
// Generate RSS items
$rssItems = '';
foreach ($posts as $post) {
$title = escapeXml($post['title']);
$excerpt = escapeXml($post['excerpt']);
$content = formatContent($post['content']);
$pubDate = date('r', strtotime($post['date']));
$link = 'https://example.com/blog/post.html?id=' . $post['id'];
$rssItems .= " <item>\n";
$rssItems .= " <title>{$title}</title>\n";
$rssItems .= " <link>{$link}</link>\n";
$rssItems .= " <guid>{$link}</guid>\n";
$rssItems .= " <pubDate>{$pubDate}</pubDate>\n";
$rssItems .= " <description><![CDATA[{$excerpt}<br/><br/>{$content}]]></description>\n";
$rssItems .= " </item>\n";
}
// Output RSS XML
echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">' . "\n";
echo " <channel>\n";
echo " <title>My Blog</title>\n";
echo " <link>https://example.com/blog</link>\n";
echo " <description>Blog posts</description>\n";
echo " <language>en-us</language>\n";
echo " <lastBuildDate>" . date('r') . "</lastBuildDate>\n";
echo " <atom:link href=\"https://example.com/blog/api/feed.php\" rel=\"self\" type=\"application/rss+xml\"/>\n";
echo $rssItems;
echo " </channel>\n";
echo "</rss>\n";
?>