123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
<?php
class Database {
public $pdo = null;
private $dbPath;
private $configFile;
private $extensionMapFile;
private $iconsFile;
public function __construct($indexerFilesDir, $baseDir = null) {
$dbDir = $indexerFilesDir . '/db';
$this->dbPath = $dbDir . '/indexer.sqlite';
$this->configFile = $indexerFilesDir . '/config.json';
$this->extensionMapFile = $indexerFilesDir . '/local_api/extensionMap.json';
$this->iconsFile = $indexerFilesDir . '/local_api/icons.json';
if (!file_exists($this->dbPath)) {
require_once dirname(__FILE__) . '/Initializer.php';
$initializer = new Initializer($indexerFilesDir, $baseDir);
$initializer->initializeDatabase(false);
}
$this->initialize();
}
private function initialize() {
try {
$this->pdo = new PDO('sqlite:' . $this->dbPath);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->createTables();
$this->syncSystemFiles();
} catch (Exception $e) {
error_log("Database initialization error: " . $e->getMessage());
throw $e;
}
}
private function createTables() {
$this->pdo->exec("CREATE TABLE IF NOT EXISTS system_config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
last_updated INTEGER NOT NULL
)");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS extension_mappings (
extension TEXT PRIMARY KEY,
indexing_setting TEXT,
viewing_setting TEXT,
last_updated INTEGER NOT NULL
)");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS icon_mappings (
identifier TEXT PRIMARY KEY,
icon_filename TEXT NOT NULL,
last_updated INTEGER NOT NULL
)");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS file_metadata (
path TEXT PRIMARY KEY,
filename TEXT NOT NULL,
extension TEXT,
size INTEGER NOT NULL,
modified INTEGER NOT NULL,
is_directory INTEGER NOT NULL,
parent_path TEXT,
indexed_at INTEGER NOT NULL,
last_verified INTEGER NOT NULL
)");
$this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_file_parent ON file_metadata(parent_path)");
$this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_file_extension ON file_metadata(extension)");
$this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_file_modified ON file_metadata(modified)");
$this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_file_directory ON file_metadata(is_directory)");
}
public function syncSystemFiles() {
static $synced = false;
if ($synced) {
return;
}
$synced = true;
if (file_exists($this->configFile)) {
$configModified = filemtime($this->configFile);
$stmt = $this->pdo->prepare("SELECT last_updated FROM system_config WHERE key = 'config_timestamp'");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result || $result['last_updated'] < $configModified) {
$configData = json_decode(file_get_contents($this->configFile), true);
if ($configData) {
$this->updateSystemConfig($configData);
}
}
}
if (file_exists($this->extensionMapFile)) {
$mapModified = filemtime($this->extensionMapFile);
$stmt = $this->pdo->prepare("SELECT last_updated FROM system_config WHERE key = 'extension_map_timestamp'");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result || $result['last_updated'] < $mapModified) {
$mapData = json_decode(file_get_contents($this->extensionMapFile), true);
if ($mapData) {
$this->updateExtensionMappings($mapData);
}
}
}
if (file_exists($this->iconsFile)) {
$iconsModified = filemtime($this->iconsFile);
$stmt = $this->pdo->prepare("SELECT last_updated FROM system_config WHERE key = 'icons_timestamp'");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result || $result['last_updated'] < $iconsModified) {
$iconsData = json_decode(file_get_contents($this->iconsFile), true);
if ($iconsData) {
$this->updateIconMappings($iconsData);
}
}
}
}
private function updateSystemConfig($configData) {
$timestamp = time();
$this->pdo->beginTransaction();
try {
$stmt = $this->pdo->prepare("INSERT OR REPLACE INTO system_config (key, value, last_updated) VALUES (?, ?, ?)");
$stmt->execute(['config_data', json_encode($configData), $timestamp]);
$stmt->execute(['config_timestamp', $timestamp, $timestamp]);
if (isset($configData['version'])) {
$stmt->execute(['version', $configData['version'], $timestamp]);
}
if (isset($configData['main'])) {
foreach ($configData['main'] as $key => $value) {
$stmt->execute(['main_' . $key, json_encode($value), $timestamp]);
}
}
$this->pdo->commit();
} catch (Exception $e) {
$this->pdo->rollBack();
error_log("Config update error: " . $e->getMessage());
}
}
private function updateExtensionMappings($mapData) {
$timestamp = time();
$this->pdo->beginTransaction();
try {
$this->pdo->exec("DELETE FROM extension_mappings");
$stmt = $this->pdo->prepare("INSERT INTO extension_mappings (extension, indexing_setting, viewing_setting, last_updated) VALUES (?, ?, ?, ?)");
if (isset($mapData['indexing'])) {
foreach ($mapData['indexing'] as $ext => $setting) {
$viewingSetting = isset($mapData['viewing'][$ext]) ? $mapData['viewing'][$ext] : null;
$stmt->execute([$ext, $setting, $viewingSetting, $timestamp]);
}
}
if (isset($mapData['viewing'])) {
foreach ($mapData['viewing'] as $ext => $setting) {
if (!isset($mapData['indexing'][$ext])) {
$stmt->execute([$ext, null, $setting, $timestamp]);
}
}
}
$stmt = $this->pdo->prepare("INSERT OR REPLACE INTO system_config (key, value, last_updated) VALUES (?, ?, ?)");
$stmt->execute(['extension_map_timestamp', $timestamp, $timestamp]);
$this->pdo->commit();
} catch (Exception $e) {
$this->pdo->rollBack();
error_log("Extension map update error: " . $e->getMessage());
}
}
private function updateIconMappings($iconsData) {
$timestamp = time();
$this->pdo->beginTransaction();
try {
$this->pdo->exec("DELETE FROM icon_mappings");
$stmt = $this->pdo->prepare("INSERT INTO icon_mappings (identifier, icon_filename, last_updated) VALUES (?, ?, ?)");
foreach ($iconsData as $identifier => $iconFile) {
$stmt->execute([$identifier, $iconFile, $timestamp]);
}
$stmt = $this->pdo->prepare("INSERT OR REPLACE INTO system_config (key, value, last_updated) VALUES (?, ?, ?)");
$stmt->execute(['icons_timestamp', $timestamp, $timestamp]);
$this->pdo->commit();
} catch (Exception $e) {
$this->pdo->rollBack();
error_log("Icons update error: " . $e->getMessage());
}
}
public function getConfig($key = null) {
try {
if ($key === null) {
$stmt = $this->pdo->prepare("SELECT value FROM system_config WHERE key = 'config_data'");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result ? json_decode($result['value'], true) : [];
} else {
$stmt = $this->pdo->prepare("SELECT value FROM system_config WHERE key = ?");
$stmt->execute([$key]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result ? json_decode($result['value'], true) : null;
}
} catch (Exception $e) {
error_log("Config get error: " . $e->getMessage());
return null;
}
}
public function getExtensionSetting($extension, $type = 'indexing') {
try {
$stmt = $this->pdo->prepare("SELECT indexing_setting, viewing_setting FROM extension_mappings WHERE extension = ?");
$stmt->execute([strtolower($extension)]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
return $type === 'indexing' ? $result['indexing_setting'] : $result['viewing_setting'];
}
return null;
} catch (Exception $e) {
error_log("Extension setting get error: " . $e->getMessage());
return null;
}
}
public function getIconMapping($identifier) {
try {
$stmt = $this->pdo->prepare("SELECT icon_filename FROM icon_mappings WHERE identifier = ?");
$stmt->execute([strtolower($identifier)]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result ? $result['icon_filename'] : null;
} catch (Exception $e) {
error_log("Icon mapping get error: " . $e->getMessage());
return null;
}
}
public function indexPath($path, $filename, $extension, $size, $modified, $isDirectory, $parentPath = null) {
try {
$timestamp = time();
$stmt = $this->pdo->prepare("INSERT OR REPLACE INTO file_metadata
(path, filename, extension, size, modified, is_directory, parent_path, indexed_at, last_verified)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$path,
$filename,
$extension ? strtolower($extension) : null,
$size,
$modified,
$isDirectory ? 1 : 0,
$parentPath,
$timestamp,
$timestamp
]);
return true;
} catch (Exception $e) {
error_log("Index path error: " . $e->getMessage());
return false;
}
}
public function getFileMetadata($path) {
try {
$stmt = $this->pdo->prepare("SELECT * FROM file_metadata WHERE path = ?");
$stmt->execute([$path]);
return $stmt->fetch(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log("Get metadata error: " . $e->getMessage());
return null;
}
}
public function getDirectoryContents($parentPath) {
try {
if ($parentPath === null || $parentPath === '' || $parentPath === 'ROOT_DIR') {
$stmt = $this->pdo->prepare("SELECT * FROM file_metadata WHERE parent_path IS NULL OR parent_path = '' OR parent_path = 'ROOT_DIR' ORDER BY is_directory DESC, filename ASC");
$stmt->execute();
} else {
$stmt = $this->pdo->prepare("SELECT * FROM file_metadata WHERE parent_path = ? ORDER BY is_directory DESC, filename ASC");
$stmt->execute([$parentPath]);
}
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log("Get directory contents error: " . $e->getMessage());
return [];
}
}
public function needsReindex($path, $currentModified) {
try {
$stmt = $this->pdo->prepare("SELECT modified FROM file_metadata WHERE path = ?");
$stmt->execute([$path]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
return true;
}
return $result['modified'] < $currentModified;
} catch (Exception $e) {
return true;
}
}
public function cleanupDeletedPaths($basePath, $existingPaths) {
try {
$this->pdo->beginTransaction();
$stmt = $this->pdo->prepare("SELECT path FROM file_metadata WHERE path = ? OR path LIKE ?");
$stmt->execute([$basePath, $basePath . '/%']);
$indexedPaths = $stmt->fetchAll(PDO::FETCH_COLUMN);
$deletedPaths = array_diff($indexedPaths, $existingPaths);
if (!empty($deletedPaths)) {
$placeholders = implode(',', array_fill(0, count($deletedPaths), '?'));
$stmt = $this->pdo->prepare("DELETE FROM file_metadata WHERE path IN ($placeholders)");
$stmt->execute(array_values($deletedPaths));
}
$this->pdo->commit();
return count($deletedPaths);
} catch (Exception $e) {
$this->pdo->rollBack();
error_log("Cleanup error: " . $e->getMessage());
return 0;
}
}
public function getStatistics() {
try {
$stats = [];
$stmt = $this->pdo->query("SELECT COUNT(*) as count FROM file_metadata WHERE is_directory = 0");
$stats['total_files'] = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
$stmt = $this->pdo->query("SELECT COUNT(*) as count FROM file_metadata WHERE is_directory = 1");
$stats['total_directories'] = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
$stmt = $this->pdo->query("SELECT SUM(size) as total FROM file_metadata WHERE is_directory = 0");
$stats['total_size'] = $stmt->fetch(PDO::FETCH_ASSOC)['total'] ?: 0;
if (file_exists($this->dbPath)) {
$stats['database_size'] = filesize($this->dbPath);
}
return $stats;
} catch (Exception $e) {
error_log("Statistics error: " . $e->getMessage());
return [];
}
}
public function optimize() {
try {
$this->pdo->exec("VACUUM");
$this->pdo->exec("ANALYZE");
return true;
} catch (Exception $e) {
error_log("Optimize error: " . $e->getMessage());
return false;
}
}
}
?>