Document the PHP api layer and fix what documenting it exposed
Writing down what each endpoint actually guarantees made the places it didn't obvious — shell arguments reaching a crontab or a bash -c unescaped, master.conf written without tmp+rename, and conf edits that could be saved without ever being parsed.
This commit is contained in:
@@ -1,7 +1,73 @@
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
// PURPOSE
|
||||
// Relocates the plugin. Points SCRIPTS_DIR in varaverk.cfg at a different Varaverk checkout
|
||||
// — the manual equivalent of what the storage-migration flow does automatically.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// varaverk.cfg is the one file whose location is fixed. Everything else the plugin reads —
|
||||
// scripts, Configurations/, Deployment/, State_Files/, data/ — is resolved relative to the
|
||||
// SCRIPTS_DIR it names, which is what makes internal-vs-appdata storage mode possible at
|
||||
// all. Rewriting that one value moves the entire plugin.
|
||||
//
|
||||
// Currently unreferenced by the UI. The settings tab drives relocation through the storage
|
||||
// migration flow, which also copies the data. This endpoint changes the pointer alone, and
|
||||
// is kept as the recovery path for a cfg that points somewhere that no longer exists.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Changes the pointer, never moves the data.
|
||||
// There is deliberately no copy step. Migration is a separate, longer operation with
|
||||
// its own confirmation; conflating the two would make a one-field edit capable of
|
||||
// deleting a directory.
|
||||
//
|
||||
// Merges into the existing cfg rather than authoring it.
|
||||
// varaverk.cfg carries GITEA_CONTAINER, GITEA_REPO_PATH, GITEA_SSH_KEY, SSH_PORT and
|
||||
// CUSTOM_SCRIPTS_DIR alongside SCRIPTS_DIR. Only the key being changed is touched.
|
||||
//
|
||||
// OPERATIONAL SAFEGUARDS
|
||||
// POST only, checked before anything is read.
|
||||
//
|
||||
// The target must actually be a Varaverk checkout.
|
||||
// is_dir() alone is not enough — it would happily accept /mnt/user or /tmp and leave
|
||||
// every subsequent page resolving conf and script paths under a directory that contains
|
||||
// none of them, with no single obvious symptom. common.sh and load_config.sh must both
|
||||
// be present, because those are the two files every script in the repo sources.
|
||||
//
|
||||
// Unrelated keys survive the write.
|
||||
// The previous version emitted a file containing SCRIPTS_DIR and nothing else, silently
|
||||
// discarding the Gitea coordinates that git_pull_execute.sh needs — so a relocation
|
||||
// would have broken both the daily pull and the UI pull button, and only at the next
|
||||
// scheduled run. The cfg is now read, one key replaced, and all keys written back.
|
||||
//
|
||||
// The write is atomic.
|
||||
// tmp + rename, so a page render or a script reading varaverk.cfg mid-save sees the old
|
||||
// file or the new one. A truncated varaverk.cfg would leave SCRIPTS_DIR undefined and
|
||||
// every path in the plugin falling back to the compiled-in default.
|
||||
//
|
||||
// Values are escaped for the ini quoting parse_ini_file() expects.
|
||||
// addslashes() on every value written, not just the new one, so a path containing a
|
||||
// quote cannot terminate the string early and corrupt the keys that follow it.
|
||||
//
|
||||
// REQUEST
|
||||
// POST scripts_dir=<absolute path to a Varaverk checkout>
|
||||
//
|
||||
// RESPONSE
|
||||
// {"ok":true,"error":null}
|
||||
// {"ok":false,"error":"POST only"|"scripts_dir is required"|"Directory does not exist"
|
||||
// |"Not a Varaverk checkout — common.sh and load_config.sh not found"
|
||||
// |"Failed to write cfg file"}
|
||||
//
|
||||
// DEPENDS ON
|
||||
// include/config.php PLUGIN_CFG
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['ok' => false, 'error' => 'POST only']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$scriptsDir = trim($_POST['scripts_dir'] ?? '');
|
||||
|
||||
if (!$scriptsDir) {
|
||||
@@ -14,11 +80,33 @@ if (!is_dir($scriptsDir)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Pointing SCRIPTS_DIR at an arbitrary directory leaves every conf and script path in the
|
||||
// plugin resolving under something that holds none of them. These two files are sourced by
|
||||
// every script in the repo, so their presence is what makes a directory a checkout.
|
||||
if (!file_exists($scriptsDir . '/common.sh') || !file_exists($scriptsDir . '/load_config.sh')) {
|
||||
echo json_encode([
|
||||
'ok' => false,
|
||||
'error' => 'Not a Varaverk checkout — common.sh and load_config.sh not found',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$cfgFile = PLUGIN_CFG;
|
||||
$cfgDir = dirname($cfgFile);
|
||||
if (!is_dir($cfgDir)) mkdir($cfgDir, 0755, true);
|
||||
|
||||
$content = 'SCRIPTS_DIR="' . addslashes($scriptsDir) . '"' . "\n";
|
||||
$ok = file_put_contents($cfgFile, $content) !== false;
|
||||
// Merge, do not author. varaverk.cfg also carries the Gitea coordinates git_pull_execute.sh
|
||||
// needs — rewriting the file with SCRIPTS_DIR alone would silently break the daily pull.
|
||||
$cfg = @parse_ini_file($cfgFile) ?: [];
|
||||
$cfg['SCRIPTS_DIR'] = $scriptsDir;
|
||||
|
||||
$content = '';
|
||||
foreach ($cfg as $k => $v) {
|
||||
$content .= $k . '="' . addslashes((string)$v) . '"' . "\n";
|
||||
}
|
||||
|
||||
$tmp = $cfgFile . '.vv.tmp';
|
||||
$ok = file_put_contents($tmp, $content) !== false && rename($tmp, $cfgFile);
|
||||
if (!$ok) @unlink($tmp);
|
||||
|
||||
echo json_encode(['ok' => $ok, 'error' => $ok ? null : 'Failed to write cfg file']);
|
||||
|
||||
Reference in New Issue
Block a user