improved debugging + health endpoint
This commit is contained in:
@ -50,6 +50,7 @@ $routes = [
|
||||
'backup' => __DIR__ . '/../handlers/backup.php',
|
||||
'dev' => __DIR__ . '/../handlers/dev.php',
|
||||
'dev/import' => __DIR__ . '/../handlers/dev.php',
|
||||
'health' => __DIR__ . '/../handlers/health.php',
|
||||
];
|
||||
|
||||
try {
|
||||
|
||||
96
common.php
96
common.php
@ -6,17 +6,67 @@ require_once __DIR__ . '/lib/text_sanitize.php';
|
||||
|
||||
/**
|
||||
* Values loaded from the project .env file (see qdb_load_dotenv).
|
||||
* Checked before getenv so a stale/wrong FPM pool variable cannot override .env.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
$GLOBALS['qdb_dotenv'] = [];
|
||||
|
||||
function qdb_env_file_path(): string {
|
||||
return __DIR__ . '/.env';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a dotenv file (cached per path). Does not use getenv/putenv.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
function qdb_parse_env_file(string $path): array {
|
||||
static $cache = [];
|
||||
if (isset($cache[$path])) {
|
||||
return $cache[$path];
|
||||
}
|
||||
$out = [];
|
||||
if (!is_readable($path)) {
|
||||
return $cache[$path] = $out;
|
||||
}
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
||||
if ($lines === false) {
|
||||
return $cache[$path] = $out;
|
||||
}
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || $line[0] === '#') {
|
||||
continue;
|
||||
}
|
||||
$eq = strpos($line, '=');
|
||||
if ($eq === false) {
|
||||
continue;
|
||||
}
|
||||
$name = trim(substr($line, 0, $eq));
|
||||
$value = trim(substr($line, $eq + 1));
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
$len = strlen($value);
|
||||
if ($len >= 2) {
|
||||
$q = $value[0];
|
||||
if (($q === '"' && $value[$len - 1] === '"') || ($q === "'" && $value[$len - 1] === "'")) {
|
||||
$value = substr($value, 1, -1);
|
||||
}
|
||||
}
|
||||
$out[$name] = $value;
|
||||
}
|
||||
return $cache[$path] = $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an environment variable (.env file first, then getenv / $_ENV / $_SERVER).
|
||||
* PHP-FPM often disables putenv(); .env is applied via qdb_dotenv + $_ENV.
|
||||
*/
|
||||
function qdb_env_get(string $name): ?string {
|
||||
$fileVars = qdb_parse_env_file(qdb_env_file_path());
|
||||
if (isset($fileVars[$name]) && $fileVars[$name] !== '') {
|
||||
return $fileVars[$name];
|
||||
}
|
||||
if (isset($GLOBALS['qdb_dotenv'][$name]) && $GLOBALS['qdb_dotenv'][$name] !== '') {
|
||||
return $GLOBALS['qdb_dotenv'][$name];
|
||||
}
|
||||
@ -42,52 +92,24 @@ function qdb_env_set(string $name, string $value): void {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load KEY=value pairs from .env in the project root.
|
||||
* Values from the file take precedence over PHP-FPM / getenv for the same keys.
|
||||
*/
|
||||
/** Load KEY=value pairs from .env into $GLOBALS['qdb_dotenv'] and $_ENV. */
|
||||
function qdb_load_dotenv(string $path): void {
|
||||
if (!is_readable($path)) {
|
||||
return;
|
||||
}
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
||||
if ($lines === false) {
|
||||
return;
|
||||
}
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || $line[0] === '#') {
|
||||
continue;
|
||||
}
|
||||
$eq = strpos($line, '=');
|
||||
if ($eq === false) {
|
||||
continue;
|
||||
}
|
||||
$name = trim(substr($line, 0, $eq));
|
||||
$value = trim(substr($line, $eq + 1));
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
$len = strlen($value);
|
||||
if ($len >= 2) {
|
||||
$q = $value[0];
|
||||
if (($q === '"' && $value[$len - 1] === '"') || ($q === "'" && $value[$len - 1] === "'")) {
|
||||
$value = substr($value, 1, -1);
|
||||
}
|
||||
}
|
||||
foreach (qdb_parse_env_file($path) as $name => $value) {
|
||||
$GLOBALS['qdb_dotenv'][$name] = $value;
|
||||
qdb_env_set($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
qdb_load_dotenv(__DIR__ . '/.env');
|
||||
qdb_load_dotenv(qdb_env_file_path());
|
||||
|
||||
// --- MASTER-KEY (Server-seitig zum Speichern der DB) ---
|
||||
// Per ENV 'QDB_MASTER_KEY' (Base64- oder ASCII-String) oder Fallback auf bisherigen Wert.
|
||||
// Per ENV 'QDB_MASTER_KEY' (Base64- oder ASCII-String).
|
||||
function get_master_key_bytes(): string {
|
||||
$env = qdb_env_get('QDB_MASTER_KEY');
|
||||
if ($env === null) {
|
||||
throw new RuntimeException('QDB_MASTER_KEY environment variable is not set. Cannot proceed without a master key.');
|
||||
$hint = is_readable(qdb_env_file_path()) ? 'missing QDB_MASTER_KEY in .env'
|
||||
: '.env not readable by PHP (' . qdb_env_file_path() . ')';
|
||||
throw new RuntimeException('QDB_MASTER_KEY is not set. ' . $hint);
|
||||
}
|
||||
$b = base64_decode($env, true);
|
||||
if ($b !== false && strlen($b) > 0) {
|
||||
|
||||
14
db_init.php
14
db_init.php
@ -194,7 +194,21 @@ function qdb_open(bool $writable = false): array {
|
||||
if ($lockFp) { flock($lockFp, LOCK_UN); fclose($lockFp); }
|
||||
throw new Exception("Could not read stored DB");
|
||||
}
|
||||
try {
|
||||
$decrypted = aes256_cbc_decrypt_bytes($storedEnc, $masterKey);
|
||||
} catch (Throwable $e) {
|
||||
error_log(sprintf(
|
||||
'qdb_open decrypt failed: path=%s realpath=%s enc_bytes=%d key_sha=%s sapi=%s env_readable=%s common=%s',
|
||||
$dbPath,
|
||||
realpath($dbPath) ?: 'none',
|
||||
strlen($storedEnc),
|
||||
hash('sha256', $masterKey),
|
||||
PHP_SAPI,
|
||||
is_readable(__DIR__ . '/.env') ? 'yes' : 'no',
|
||||
realpath(__DIR__ . '/common.php') ?: __DIR__ . '/common.php'
|
||||
));
|
||||
throw $e;
|
||||
}
|
||||
if (file_put_contents($tmpDb, $decrypted) === false) {
|
||||
if ($lockFp) { flock($lockFp, LOCK_UN); fclose($lockFp); }
|
||||
throw new Exception("Could not write temp DB");
|
||||
|
||||
42
handlers/health.php
Normal file
42
handlers/health.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/health — verify FPM sees the same project root, key, and DB as CLI.
|
||||
*/
|
||||
if ($method !== 'GET') {
|
||||
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
|
||||
}
|
||||
|
||||
$dbOpenOk = false;
|
||||
$dbError = null;
|
||||
try {
|
||||
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
|
||||
qdb_discard($tmpDb, $lockFp);
|
||||
$dbOpenOk = true;
|
||||
} catch (Throwable $e) {
|
||||
$dbError = $e->getMessage();
|
||||
}
|
||||
|
||||
$keySha = null;
|
||||
$keyError = null;
|
||||
try {
|
||||
$keySha = hash('sha256', get_master_key_bytes());
|
||||
} catch (Throwable $e) {
|
||||
$keyError = $e->getMessage();
|
||||
}
|
||||
|
||||
json_success([
|
||||
'sapi' => PHP_SAPI,
|
||||
'projectRoot' => realpath(__DIR__ . '/..') ?: (__DIR__ . '/..'),
|
||||
'commonFile' => realpath(__DIR__ . '/../common.php') ?: (__DIR__ . '/../common.php'),
|
||||
'commonMtime' => @filemtime(__DIR__ . '/../common.php') ?: 0,
|
||||
'envFile' => qdb_env_file_path(),
|
||||
'envReadable' => is_readable(qdb_env_file_path()),
|
||||
'keySha256' => $keySha,
|
||||
'keyError' => $keyError,
|
||||
'dbPath' => QDB_PATH,
|
||||
'dbRealpath' => realpath(QDB_PATH) ?: null,
|
||||
'dbBytes' => is_file(QDB_PATH) ? filesize(QDB_PATH) : 0,
|
||||
'dbOpenOk' => $dbOpenOk,
|
||||
'dbError' => $dbError,
|
||||
'expectedKeySha'=> 'e584e16eb81fbb4f1e3aac965b5225c5fefabc2a67ea20e26e55671c7d9f1ae7',
|
||||
]);
|
||||
Reference in New Issue
Block a user