fix: auto-create data dir on first Docker run (HY000[14])

When a Docker named volume is first mounted at /var/www/html/data,
the directory may be owned by root (the volume is created empty before
the image's chown step applies). This caused PDO::__construct to throw:
  SQLSTATE[HY000][14] unable to open database file

Fix: _ensureDataDir() checks/creates the directory and attempts chmod
before every getDB() call. On subsequent calls the is_dir+is_writable
checks are O(1) stat calls with no overhead.

Fixes #34 (also closes #28 #29 #30 #31 #32 #33)
This commit is contained in:
dadaloop82
2026-05-11 15:54:40 +00:00
parent a21b54deaa
commit 38c6c5aac3
+30
View File
@@ -9,7 +9,37 @@
define('DB_PATH', __DIR__ . '/../data/evershelf.db');
/**
* Ensure the data directory exists and is writable by the web-server user.
* This is needed when a Docker volume is first mounted: the image's chown
* step is applied to the image layer, but a fresh named volume starts empty
* (owned by root), making SQLite's PDO::__construct fail with HY000[14].
*/
function _ensureDataDir(): void {
$dir = dirname(DB_PATH);
if (!is_dir($dir)) {
if (!mkdir($dir, 0775, true) && !is_dir($dir)) {
throw new \RuntimeException("Cannot create data directory: $dir");
}
}
if (!is_writable($dir)) {
// Try to fix permissions (only works when running as root, e.g. first boot)
@chmod($dir, 0775);
if (!is_writable($dir)) {
throw new \RuntimeException(
"Data directory is not writable: $dir — run: chown -R www-data:www-data $dir"
);
}
}
// Ensure backups sub-directory exists too
$backups = $dir . '/backups';
if (!is_dir($backups)) {
@mkdir($backups, 0775, true);
}
}
function getDB(): PDO {
_ensureDataDir();
$isNew = !file_exists(DB_PATH);
$db = new PDO('sqlite:' . DB_PATH);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);