Initial commit: RSS feed dashboard aggregating YouTube, Reddit and RT
- PHP 8 + PDO SQLite cache with on-demand TTL refresh and dedup by link hash - laminas-feed + Guzzle for RSS/Atom parsing and HTTP - Bootstrap 5 UI with source and time-range filters, AJAX load-more - User cron-free deployment under Apache/php-fpm; .htaccess hardening
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App;
|
||||
|
||||
use PDO;
|
||||
|
||||
final class Database
|
||||
{
|
||||
public static function connect(string $path): PDO
|
||||
{
|
||||
$dir = dirname($path);
|
||||
if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
|
||||
throw new \RuntimeException(sprintf('Unable to create database directory "%s".', $dir));
|
||||
}
|
||||
|
||||
$pdo = new PDO('sqlite:' . $path, null, null, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
]);
|
||||
|
||||
$pdo->exec('PRAGMA journal_mode = WAL');
|
||||
$pdo->exec('PRAGMA busy_timeout = 5000');
|
||||
$pdo->exec('PRAGMA foreign_keys = ON');
|
||||
|
||||
self::migrate($pdo);
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
private static function migrate(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec(<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS items (
|
||||
id TEXT PRIMARY KEY,
|
||||
source TEXT NOT NULL,
|
||||
feed_label TEXT,
|
||||
feed_url TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
link TEXT NOT NULL,
|
||||
author TEXT,
|
||||
published_at TEXT,
|
||||
description TEXT,
|
||||
thumbnail TEXT,
|
||||
fetched_at TEXT NOT NULL
|
||||
)
|
||||
SQL);
|
||||
|
||||
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_items_published ON items (published_at DESC)');
|
||||
$pdo->exec('CREATE INDEX IF NOT EXISTS idx_items_source ON items (source, published_at DESC)');
|
||||
|
||||
$pdo->exec(<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS feeds (
|
||||
url TEXT PRIMARY KEY,
|
||||
source TEXT NOT NULL,
|
||||
label TEXT,
|
||||
last_attempt_at TEXT,
|
||||
last_success_at TEXT,
|
||||
last_error TEXT
|
||||
)
|
||||
SQL);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user