diff --git a/.gitignore b/.gitignore index 203e832..412113d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ composer.lock .env app/logs/*.log app/cache/*.gz +app/cache/database/*.sql TODO.md node_modules diff --git a/Dockerfile b/Dockerfile index 8a2ebfb..4b3a38e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,10 +11,9 @@ RUN apt-get update && apt-get install -y \ git \ htop \ libzip-dev \ - libhiredis-dev \ - && docker-php-ext-install zip opcache \ - && pecl install redis \ - && docker-php-ext-enable redis opcache \ + sqlite3 \ + && docker-php-ext-install zip opcache pdo_sqlite \ + && docker-php-ext-enable opcache \ && apt-get clean && rm -rf /var/lib/apt/lists/* # Stage 1: Build stage @@ -48,8 +47,8 @@ COPY default.conf /etc/nginx/sites-available/default COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh -# Create cache and logs folders -RUN mkdir -p /app/cache /app/logs +# Create cache, database, and logs folders +RUN mkdir -p /app/cache /app/cache/database /app/logs # Configure base permissions for /app directory RUN chown -R www-data:www-data /app \ diff --git a/app/cache/database/.gitkeep b/app/cache/database/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/cache/database/.sqlite b/app/cache/database/.sqlite new file mode 100644 index 0000000..f16b0f8 Binary files /dev/null and b/app/cache/database/.sqlite differ diff --git a/app/config.php b/app/config.php index 3821e25..d431cd4 100644 --- a/app/config.php +++ b/app/config.php @@ -38,11 +38,6 @@ try { define('CACHE_DIR', __DIR__ . '/cache'); define('LANGUAGE', $_ENV['LANGUAGE'] ?? 'pt-br'); - // Redis connection settings - define('REDIS_HOST', $_ENV['REDIS_HOST'] ?? 'localhost'); - define('REDIS_PORT', $_ENV['REDIS_PORT'] ?? 6379); - define('REDIS_PREFIX', $_ENV['REDIS_PREFIX'] ?? 'marreta:'); - // Logging configuration define('LOG_LEVEL', $_ENV['LOG_LEVEL'] ?? 'WARNING'); // DEBUG, INFO, WARNING, ERROR, CRITICAL define('LOG_DAYS_TO_KEEP', 7); diff --git a/app/inc/Cache.php b/app/inc/Cache.php index c02362f..4824800 100644 --- a/app/inc/Cache.php +++ b/app/inc/Cache.php @@ -5,7 +5,7 @@ namespace Inc; use Inc\Cache\CacheStorageInterface; use Inc\Cache\DiskStorage; use Inc\Cache\S3Storage; -use Inc\Cache\RedisStorage; +use Inc\Cache\SQLiteStorage; /** * System cache management with multiple storage backends (disk/S3) @@ -17,18 +17,18 @@ class Cache /** @var CacheStorageInterface Cache storage implementation */ private $storage; - /** @var RedisStorage Redis instance for file counting */ - private $redisStorage; + /** @var SQLiteStorage SQLite instance for file counting */ + private $sqliteStorage; /** * Initializes storage based on configuration * Uses S3Storage if configured and enabled - * Defaults to DiskStorage otherwise + * Defaults to SQLiteStorage otherwise (which delegates to DiskStorage) */ public function __construct() { - $this->redisStorage = new RedisStorage(CACHE_DIR); - + $this->sqliteStorage = new SQLiteStorage(CACHE_DIR); + if (defined('S3_CACHE_ENABLED') && S3_CACHE_ENABLED === true) { $this->storage = new S3Storage([ 'key' => S3_ACCESS_KEY, @@ -40,14 +40,14 @@ class Cache 'endpoint' => defined('S3_ENDPOINT') ? S3_ENDPOINT : null ]); } else { - $this->storage = new DiskStorage(CACHE_DIR); + $this->storage = $this->sqliteStorage; } } /** Gets total number of cached files */ public function getCacheFileCount(): int { - return $this->redisStorage->countCacheFiles(); + return $this->sqliteStorage->countCacheFiles(); } /** diff --git a/app/inc/Cache/RedisStorage.php b/app/inc/Cache/RedisStorage.php deleted file mode 100644 index b1634f1..0000000 --- a/app/inc/Cache/RedisStorage.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php - -namespace Inc\Cache; - -use Redis; - -/** - * Redis-based cache storage implementation - * Provides cache storage and file counting functionality using Redis - */ -class RedisStorage implements CacheStorageInterface -{ - /** - * @var \Redis|null Redis client instance - */ - private $redis; - - /** - * @var string Cache directory for file counting - */ - private $cacheDir; - - /** - * Class constructor - * @param string $cacheDir Base directory for cache storage - */ - public function __construct(string $cacheDir) - { - $this->cacheDir = $cacheDir; - - // Try to initialize Redis connection - try { - $this->redis = new \Redis(); - $this->redis->connect(REDIS_HOST, REDIS_PORT, 2.5); - $this->redis->setOption(\Redis::OPT_PREFIX, REDIS_PREFIX); - } catch (\Exception $e) { - $this->redis = null; - } - } - - /** - * Counts the number of files in the cache directory - * @return int Number of files in the cache directory - */ - public function countCacheFiles(): int - { - $cacheCountKey = 'cache_file_count'; - - if ($this->redis !== null) { - $cachedCount = $this->redis->get($cacheCountKey); - if ($cachedCount !== false) { - return (int)$cachedCount; - } - } - - $fileCount = 0; - $iterator = new \FilesystemIterator($this->cacheDir); - foreach ($iterator as $file) { - if ($file->isFile() && $file->getExtension() === 'gz') { - $fileCount++; - } - } - - if ($this->redis !== null) { - $this->redis->set($cacheCountKey, $fileCount); - } - - return $fileCount; - } - - /** - * Updates the file count in Redis - * @param int $count Number of files - */ - public function updateCacheFileCount(int $count): void - { - if ($this->redis !== null) { - $this->redis->set('cache_file_count', $count); - } - } - - /** - * Checks if cache exists for a given ID - * @param string $id Cache ID - * @return bool True if cache exists, false otherwise - */ - public function exists(string $id): bool - { - return $this->redis !== null ? $this->redis->exists($id) : false; - } - - /** - * Retrieves cached content - * @param string $id Cache ID - * @return string|null Cached content or null if not found - */ - public function get(string $id): ?string - { - if ($this->redis === null) { - return null; - } - - $content = $this->redis->get($id); - return $content === false ? null : $content; - } - - /** - * Stores content in cache - * @param string $id Cache ID - * @param string $content Content to be stored - * @return bool True if successful, false otherwise - */ - public function set(string $id, string $content): bool - { - if ($this->redis === null) { - return false; - } - - $result = $this->redis->set($id, $content); - - if ($result) { - $currentCount = $this->redis->get('cache_file_count') ?: 0; - $this->redis->set('cache_file_count', $currentCount + 1); - } - - return $result; - } -} \ No newline at end of file diff --git a/app/inc/Cache/SQLiteStorage.php b/app/inc/Cache/SQLiteStorage.php new file mode 100644 index 0000000..3268267 --- /dev/null +++ b/app/inc/Cache/SQLiteStorage.php @@ -0,0 +1,177 @@ +<?php + +namespace Inc\Cache; + +use PDO; +use PDOException; + +/** + * SQLite-based cache storage implementation + * Provides file counting functionality using SQLite + * Delegates actual cache storage to DiskStorage + */ +class SQLiteStorage implements CacheStorageInterface +{ + /** + * @var PDO|null SQLite connection + */ + private $db; + + /** + * @var string Cache directory for file counting + */ + private $cacheDir; + + /** + * @var string Path to SQLite database file + */ + private $dbPath; + + /** + * @var DiskStorage Disk storage for cache entries + */ + private $diskStorage; + + /** + * Class constructor + * @param string $cacheDir Base directory for cache storage + */ + public function __construct(string $cacheDir) + { + $this->cacheDir = $cacheDir; + $this->diskStorage = new DiskStorage($cacheDir); + + // Ensure database directory exists + $dbDir = $cacheDir . '/database'; + if (!is_dir($dbDir)) { + mkdir($dbDir, 0755, true); + } + + $this->dbPath = $dbDir . '/.sqlite'; + + // Try to initialize SQLite connection + try { + $this->db = new PDO('sqlite:' . $this->dbPath); + $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Create tables if they don't exist + $this->initDatabase(); + + // If database file was just created, count cache files + if (!file_exists($this->dbPath) || filesize($this->dbPath) < 1024) { + $this->countCacheFiles(); + } + } catch (PDOException $e) { + $this->db = null; + } + } + + /** + * Initialize database tables + */ + private function initDatabase(): void + { + $this->db->exec(" + CREATE TABLE IF NOT EXISTS stats ( + key TEXT PRIMARY KEY, + value INTEGER NOT NULL + ) + "); + } + + /** + * Counts the number of files in the cache directory + * @return int Number of files in the cache directory + */ + public function countCacheFiles(): int + { + if ($this->db !== null) { + try { + $stmt = $this->db->query("SELECT value FROM stats WHERE key = 'count'"); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result) { + return (int)$result['value']; + } + } catch (PDOException $e) { + // Continue to count files if query fails + } + } + + $fileCount = 0; + $iterator = new \FilesystemIterator($this->cacheDir); + foreach ($iterator as $file) { + if ($file->isFile() && $file->getExtension() === 'gz') { + $fileCount++; + } + } + + if ($this->db !== null) { + $this->updateCacheFileCount($fileCount); + } + + return $fileCount; + } + + /** + * Updates the file count in SQLite + * @param int $count Number of files + */ + public function updateCacheFileCount(int $count): void + { + if ($this->db !== null) { + try { + $stmt = $this->db->prepare(" + INSERT OR REPLACE INTO stats (key, value) + VALUES ('count', :count) + "); + $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->execute(); + } catch (PDOException $e) { + // Silently fail if update fails + } + } + } + + /** + * Checks if cache exists for a given ID + * Delegates to DiskStorage + * @param string $id Cache ID + * @return bool True if cache exists, false otherwise + */ + public function exists(string $id): bool + { + return $this->diskStorage->exists($id); + } + + /** + * Retrieves cached content + * Delegates to DiskStorage + * @param string $id Cache ID + * @return string|null Cached content or null if not found + */ + public function get(string $id): ?string + { + return $this->diskStorage->get($id); + } + + /** + * Stores content in cache + * Delegates to DiskStorage and updates file count + * @param string $id Cache ID + * @param string $content Content to be stored + * @return bool True if successful, false otherwise + */ + public function set(string $id, string $content): bool + { + $result = $this->diskStorage->set($id, $content); + + if ($result) { + // Increment cache file count + $currentCount = $this->countCacheFiles(); + $this->updateCacheFileCount($currentCount + 1); + } + + return $result; + } +} \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index bc3ea5c..25d2580 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -54,6 +54,7 @@ log_success "Environment variables configured" log_info "Adjusting directory permissions..." mkdir -p /app/cache /app/logs # Ensures directories exist +mkdir -p /app/cache/database chown -R www-data:www-data /app/cache /app/logs chmod -R 775 /app/cache /app/logs