mirror of
https://github.com/Terranom674/Piwigo_Bratonien_Tools.git
synced 2026-09-19 10:34:30 +00:00
Compare commits
20 Commits
54a9c97525
...
abd0cc5d21
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abd0cc5d21 | ||
|
|
fced2eac49 | ||
|
|
14aac0d7a4 | ||
|
|
3f2c1f8ff7 | ||
|
|
7310d70b93 | ||
|
|
ae73840d40 | ||
|
|
a0195b499c | ||
|
|
0372a74ae9 | ||
|
|
dbcc55351c | ||
|
|
f02d2f0239 | ||
|
|
2617175158 | ||
|
|
362d24b89a | ||
|
|
459f233b57 | ||
|
|
1368c8e5b7 | ||
|
|
662d22c533 | ||
|
|
f9188ac5db | ||
|
|
5aab3e52ed | ||
|
|
6bd14d2b9b | ||
|
|
a63d5fe3c3 | ||
|
|
84456de512 |
154
include/cache_clear_atomic.inc.php
Normal file
154
include/cache_clear_atomic.inc.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
if (!defined('PHPWG_ROOT_PATH'))
|
||||
{
|
||||
die('Hacking attempt!');
|
||||
}
|
||||
|
||||
function bratonien_tools_atomic_cache_remove_tree($root, array &$failed)
|
||||
{
|
||||
if (!file_exists($root) && !is_link($root)) return;
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $item)
|
||||
{
|
||||
$path = $item->getPathname();
|
||||
if ($item->isLink() || $item->isFile())
|
||||
{
|
||||
if (!@unlink($path)) $failed[] = $path;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($item->isDir() && !@rmdir($path))
|
||||
{
|
||||
$failed[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!@rmdir($root) && is_dir($root)) $failed[] = $root;
|
||||
}
|
||||
|
||||
function bratonien_tools_clear_image_cache_atomic()
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (!defined('PWG_DERIVATIVE_DIR'))
|
||||
{
|
||||
throw new RuntimeException('PWG_DERIVATIVE_DIR ist nicht definiert.');
|
||||
}
|
||||
|
||||
if (bratonien_tools_main_cache_process_active() || bratonien_tools_main_cache_is_running())
|
||||
{
|
||||
bratonien_tools_request_main_cache_cancel();
|
||||
if (!bratonien_tools_wait_main_cache_stopped(10.0))
|
||||
{
|
||||
throw new RuntimeException('Der laufende Cache-Aufbau konnte noch nicht beendet werden. Bitte den Abbruch kurz abschließen lassen und erneut leeren.');
|
||||
}
|
||||
}
|
||||
@unlink(bratonien_tools_main_cache_cancel_file());
|
||||
|
||||
$piwigo_root = realpath(PHPWG_ROOT_PATH);
|
||||
if ($piwigo_root === false)
|
||||
{
|
||||
throw new RuntimeException('Piwigo-Root konnte für die Cache-Sicherheitsprüfung nicht aufgelöst werden.');
|
||||
}
|
||||
|
||||
$cache_root = rtrim(PHPWG_ROOT_PATH, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.trim(PWG_DERIVATIVE_DIR, '/\\');
|
||||
$real_cache_root = realpath($cache_root);
|
||||
if ($real_cache_root === false || !is_dir($real_cache_root))
|
||||
{
|
||||
throw new RuntimeException('Bildcache-Verzeichnis wurde nicht gefunden: '.$cache_root);
|
||||
}
|
||||
|
||||
$root_prefix = rtrim($piwigo_root, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
|
||||
if (strpos(rtrim($real_cache_root, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR, $root_prefix) !== 0)
|
||||
{
|
||||
throw new RuntimeException('Bildcache liegt außerhalb der Piwigo-Installation. Abbruch.');
|
||||
}
|
||||
if (rtrim($real_cache_root, DIRECTORY_SEPARATOR) === rtrim($piwigo_root, DIRECTORY_SEPARATOR))
|
||||
{
|
||||
throw new RuntimeException('Bildcache-Pfad entspricht dem Piwigo-Root. Sicherheitsabbruch.');
|
||||
}
|
||||
|
||||
$before = bratonien_tools_scan_image_cache($real_cache_root);
|
||||
$parent = dirname($real_cache_root);
|
||||
if (!is_dir($parent) || !is_writable($parent))
|
||||
{
|
||||
throw new RuntimeException('Übergeordnetes Bildcache-Verzeichnis ist nicht beschreibbar: '.$parent);
|
||||
}
|
||||
|
||||
$detached = $parent.'/.'.basename($real_cache_root).'.bratonien-clear-'.date('YmdHis').'-'.bin2hex(random_bytes(4));
|
||||
if (file_exists($detached) || is_link($detached))
|
||||
{
|
||||
throw new RuntimeException('Temporäres Cache-Auslagerungsverzeichnis existiert bereits. Abbruch.');
|
||||
}
|
||||
|
||||
// Der gesamte bisherige Cache wird in einem einzigen Rename aus dem aktiven
|
||||
// Piwigo-Pfad entfernt. Neue Requests schreiben danach nur noch in den neu
|
||||
// angelegten Cachebaum und können nicht mit dem Löschen des Altbestands
|
||||
// konkurrieren.
|
||||
if (!@rename($real_cache_root, $detached))
|
||||
{
|
||||
throw new RuntimeException('Bildcache konnte nicht atomar aus dem aktiven Pfad ausgelagert werden.');
|
||||
}
|
||||
|
||||
$mode = isset($conf['chmod_value']) ? (int)$conf['chmod_value'] : 0755;
|
||||
$umask = umask(0);
|
||||
$created = @mkdir($real_cache_root, $mode, true);
|
||||
umask($umask);
|
||||
if (!$created && !is_dir($real_cache_root))
|
||||
{
|
||||
// Der aktive Pfad muss in diesem Fehlerfall wiederhergestellt werden.
|
||||
if (!file_exists($real_cache_root) && @rename($detached, $real_cache_root))
|
||||
{
|
||||
throw new RuntimeException('Neuer Bildcache konnte nicht angelegt werden; der bisherige Cache wurde vollständig wiederhergestellt.');
|
||||
}
|
||||
throw new RuntimeException('Neuer Bildcache konnte nicht angelegt und der bisherige Cache nicht automatisch wiederhergestellt werden. Manueller Eingriff erforderlich.');
|
||||
}
|
||||
|
||||
@chmod($real_cache_root, $mode);
|
||||
@file_put_contents($real_cache_root.'/index.htm', 'Not allowed!');
|
||||
|
||||
$failed = array();
|
||||
bratonien_tools_atomic_cache_remove_tree($detached, $failed);
|
||||
|
||||
$active = bratonien_tools_scan_image_cache($real_cache_root);
|
||||
|
||||
bratonien_tools_write_main_cache_status(array(
|
||||
'state'=>'idle',
|
||||
'message'=>'Bildcache wurde atomar geleert. Kein manueller Cache-Aufbau aktiv.',
|
||||
));
|
||||
|
||||
if ($failed)
|
||||
{
|
||||
throw new RuntimeException(sprintf(
|
||||
'Der aktive Bildcache wurde erfolgreich geleert und neu angelegt, aber %d Datei(en)/Verzeichnis(se) des ausgelagerten Altbestands konnten nicht entfernt werden. Erste problematische Stelle: %s',
|
||||
count($failed),
|
||||
$failed[0]
|
||||
));
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
'Bildcache atomar geleert: %d alte Datei(en) (%s) entfernt, davon %d Custom-Derivate.',
|
||||
$before['files'],
|
||||
bratonien_tools_format_bytes($before['bytes']),
|
||||
$before['custom']
|
||||
);
|
||||
|
||||
if ($active['files'] > 0)
|
||||
{
|
||||
$message .= sprintf(
|
||||
' Während bzw. unmittelbar nach dem Umschalten wurden bereits %d neue Derivatdatei(en) durch laufende Anfragen erzeugt; diese gehören zum neuen Cache und sind kein Rest des gelöschten Bestands.',
|
||||
$active['files']
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$message .= ' Der neu aktive Cache ist zum Abschluss der Prüfung leer.';
|
||||
}
|
||||
|
||||
return array('message'=>$message);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ if (!defined('PHPWG_ROOT_PATH'))
|
||||
}
|
||||
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'tools/image_cache.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/cache_clear_atomic.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'tools/watermark.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'tools/watermark_profiles.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'tools/watermark_settings.inc.php');
|
||||
@@ -28,7 +29,7 @@ require_once(BRATONIEN_TOOLS_PATH . 'include/webdav_warmup_settings.inc.php');
|
||||
function bratonien_tools_get_tools()
|
||||
{
|
||||
return array(
|
||||
'image_cache_clear' => array('handler' => 'bratonien_tools_clear_image_cache'),
|
||||
'image_cache_clear' => array('handler' => 'bratonien_tools_clear_image_cache_atomic'),
|
||||
'image_cache_build' => array('handler' => 'bratonien_tools_start_main_cache_build'),
|
||||
'image_cache_cancel' => array('handler' => 'bratonien_tools_cancel_main_cache_build'),
|
||||
'image_cache_worker_settings' => array('handler' => 'bratonien_tools_save_cache_worker_settings'),
|
||||
|
||||
66
include/webdav_cache_validation.inc.php
Normal file
66
include/webdav_cache_validation.inc.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
if (!defined('PHPWG_ROOT_PATH'))
|
||||
{
|
||||
die('Hacking attempt!');
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_derivative_cache_valid($target_path, $derivative, $params=null, &$reason=null)
|
||||
{
|
||||
$reason = '';
|
||||
$target_path = (string)$target_path;
|
||||
if ($target_path === '' || !is_file($target_path) || !is_readable($target_path))
|
||||
{
|
||||
$reason = 'missing';
|
||||
return false;
|
||||
}
|
||||
|
||||
$cache_root = PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR;
|
||||
if (strpos($target_path, $cache_root) !== 0)
|
||||
{
|
||||
$reason = 'outside-cache';
|
||||
return false;
|
||||
}
|
||||
|
||||
clearstatcache(true, $target_path);
|
||||
$bytes = @filesize($target_path);
|
||||
if ($bytes === false || $bytes < 1)
|
||||
{
|
||||
$reason = 'empty';
|
||||
return false;
|
||||
}
|
||||
|
||||
$actual = @getimagesize($target_path);
|
||||
if (!is_array($actual) || (int)($actual[0] ?? 0) < 1 || (int)($actual[1] ?? 0) < 1)
|
||||
{
|
||||
$reason = 'not-image';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_object($derivative) && method_exists($derivative, 'get_size'))
|
||||
{
|
||||
$expected = $derivative->get_size();
|
||||
$expected_width = (int)($expected[0] ?? 0);
|
||||
$expected_height = (int)($expected[1] ?? 0);
|
||||
if ($expected_width > 0 && $expected_height > 0)
|
||||
{
|
||||
if ((int)$actual[0] !== $expected_width || (int)$actual[1] !== $expected_height)
|
||||
{
|
||||
$reason = 'dimension-mismatch:'.(int)$actual[0].'x'.(int)$actual[1].'!='.$expected_width.'x'.$expected_height;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_object($params) && isset($params->last_mod_time))
|
||||
{
|
||||
$mtime = @filemtime($target_path);
|
||||
if ($mtime === false || (int)$mtime < (int)$params->last_mod_time)
|
||||
{
|
||||
$reason = 'stale-params';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$reason = 'valid';
|
||||
return true;
|
||||
}
|
||||
@@ -10,7 +10,9 @@ function bratonien_tools_webdav_materialize_source_info($image_id)
|
||||
|
||||
$image_id = (int)$image_id;
|
||||
if ($image_id < 1) return null;
|
||||
if (array_key_exists($image_id, $cache)) return $cache[$image_id];
|
||||
$warmup_cli = PHP_SAPI === 'cli'
|
||||
&& strpos((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 'Bratonien-WebDAV-Cache-Warmup/') === 0;
|
||||
if (!$warmup_cli && array_key_exists($image_id, $cache)) return $cache[$image_id];
|
||||
|
||||
$result = pwg_query('SELECT path, coi FROM '.IMAGES_TABLE.' WHERE id='.$image_id.' LIMIT 1');
|
||||
if (!pwg_db_num_rows($result)) return $cache[$image_id] = null;
|
||||
@@ -126,6 +128,7 @@ function bratonien_tools_webdav_materialize_source_info($image_id)
|
||||
'fileid'=>$fileid,
|
||||
'width'=>$width,
|
||||
'height'=>$height,
|
||||
'state_dir'=>$state_dir,
|
||||
'coi'=>$row['coi'] ?? null,
|
||||
);
|
||||
}
|
||||
@@ -244,7 +247,7 @@ function bratonien_tools_webdav_materialize_after_signature($image_id, $variant,
|
||||
|
||||
function bratonien_tools_webdav_materialize_derivative_url($image_id, $variant, array $info, $after_url='')
|
||||
{
|
||||
$url = get_root_url().'plugins/'.BRATONIEN_TOOLS_ID.'/webdav-derivative.php?id='.(int)$image_id.'&variant='.rawurlencode($variant);
|
||||
$url = get_root_url().'plugins/'.BRATONIEN_TOOLS_ID.'/webdav-derivative-guard.php?id='.(int)$image_id.'&variant='.rawurlencode($variant);
|
||||
if ($after_url !== '')
|
||||
{
|
||||
$url .= '&after='.rawurlencode(bratonien_tools_webdav_materialize_b64url_encode($after_url));
|
||||
|
||||
@@ -32,6 +32,25 @@ function bratonien_tools_get_webdav_warmup_settings()
|
||||
return $settings;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_warmup_missing_baselines()
|
||||
{
|
||||
if (!function_exists('bratonien_tools_nc_connector_connections') || !function_exists('bratonien_tools_nc_connector_is_webdav'))
|
||||
{
|
||||
return array('Connector-Verbindungen konnten nicht geprüft werden.');
|
||||
}
|
||||
|
||||
$missing = array();
|
||||
foreach (bratonien_tools_nc_connector_connections() as $connection)
|
||||
{
|
||||
if (empty($connection['enabled']) || !bratonien_tools_nc_connector_is_webdav($connection)) continue;
|
||||
$connection_id = (int)($connection['id'] ?? 0);
|
||||
if ($connection_id < 1) continue;
|
||||
$state_file = PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-webdav-warmup.connection-'.$connection_id.'.json';
|
||||
if (!is_file($state_file) || !is_readable($state_file)) $missing[] = '#'.$connection_id;
|
||||
}
|
||||
return $missing;
|
||||
}
|
||||
|
||||
function bratonien_tools_save_webdav_warmup_settings()
|
||||
{
|
||||
$current = bratonien_tools_get_webdav_warmup_settings();
|
||||
@@ -41,6 +60,17 @@ function bratonien_tools_save_webdav_warmup_settings()
|
||||
$batch_size = max(1, min(50, $batch_size));
|
||||
$periodic_hours = max(1, min(168, $periodic_hours));
|
||||
|
||||
if ($enabled && empty($current['enabled']))
|
||||
{
|
||||
$missing = bratonien_tools_webdav_warmup_missing_baselines();
|
||||
if ($missing)
|
||||
{
|
||||
throw new RuntimeException(
|
||||
'WebDAV-Cache-Warmup bleibt deaktiviert. Bitte zuerst „Jetzt prüfen“ ausführen, damit der bestehende produktive Bestand nur als Ausgangszustand erfasst wird. Fehlende Baseline: '.implode(', ', $missing)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$payload = array(
|
||||
'enabled'=>$enabled,
|
||||
'batch_size'=>$batch_size,
|
||||
|
||||
@@ -35,6 +35,7 @@ if (!defined('BRATONIEN_TOOLS_PATH'))
|
||||
|
||||
require_once(BRATONIEN_TOOLS_PATH.'tools/image_cache.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH.'include/watermark_engine.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH.'include/webdav_materialize_runtime.inc.php');
|
||||
require_once(PHPWG_ROOT_PATH.'include/derivative.inc.php');
|
||||
require_once(PHPWG_ROOT_PATH.'admin/include/image.class.php');
|
||||
|
||||
@@ -229,6 +230,7 @@ function bratonien_tools_cache_builder_worker($worker_index, $worker_count, $run
|
||||
$source_path = $src->get_path();
|
||||
$image_id = (int)$image_row['id'];
|
||||
$metadata = null;
|
||||
$webdav_source = bratonien_tools_webdav_materialize_source_info($image_id);
|
||||
|
||||
foreach ($variants as $variant_name => $requested_params)
|
||||
{
|
||||
@@ -248,6 +250,28 @@ function bratonien_tools_cache_builder_worker($worker_index, $worker_count, $run
|
||||
{
|
||||
$derivative = new DerivativeImage($requested_params, $src);
|
||||
$target_path = $derivative->get_path();
|
||||
|
||||
// WebDAV-Connector-Bilder besitzen absichtlich nur einen Placeholder als
|
||||
// lokale Quelle. Der alte Full-Cache-Builder darf diesen niemals direkt
|
||||
// rendern. Fehlende Connector-Derivate gehören ausschließlich in den
|
||||
// materialisierenden WebDAV-Warmup-/On-Demand-Pfad.
|
||||
if ($webdav_source)
|
||||
{
|
||||
if (
|
||||
strpos($target_path, PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR) === 0
|
||||
&& is_file($target_path)
|
||||
&& is_readable($target_path)
|
||||
)
|
||||
{
|
||||
$cached++;
|
||||
}
|
||||
else
|
||||
{
|
||||
$skipped++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($target_path, PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR) !== 0)
|
||||
{
|
||||
$skipped++;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Bratonien Tools
|
||||
Version: 0.9.7.1.8
|
||||
Version: 0.9.7.1.11
|
||||
Description: Erweiterbare Administrationswerkzeuge fuer die Bratonien-Piwigo-Installation.
|
||||
Plugin URI: https://github.com/Terranom674/Piwigo_Bratonien_Tools
|
||||
Author: Bratonien
|
||||
@@ -17,6 +17,7 @@ define('BRATONIEN_TOOLS_PATH', PHPWG_PLUGINS_PATH . BRATONIEN_TOOLS_ID . '/');
|
||||
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/watermark_runtime.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/webdav_image_runtime.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/webdav_cache_validation.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/webdav_materialize_runtime.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/public_selection.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/picture_navigation.inc.php');
|
||||
|
||||
@@ -19,7 +19,7 @@ if ($request === '' || strpos($request, '/i.php?/') !== 0)
|
||||
exit(2);
|
||||
}
|
||||
|
||||
$piwigo_root = realpath(dirname(__DIR__, 3));
|
||||
$piwigo_root = realpath(dirname(__DIR__, 4));
|
||||
if ($piwigo_root === false || !is_file($piwigo_root.'/i.php'))
|
||||
{
|
||||
fwrite(STDERR, "Piwigo i.php wurde nicht gefunden.\n");
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
STATE_DIR="${1:-}"
|
||||
shift || true
|
||||
|
||||
[[ -n "$STATE_DIR" ]] || { echo "Connector-State-Verzeichnis fehlt." >&2; exit 2; }
|
||||
[[ $# -gt 0 ]] || { echo "Warmup-Kommando fehlt." >&2; exit 2; }
|
||||
|
||||
mkdir -p -- "$STATE_DIR"
|
||||
LOCK_FILE="$STATE_DIR/webdav-sync.lock"
|
||||
PRIORITY_FILE="$STATE_DIR/webdav-cache-warmup-priority-sync"
|
||||
|
||||
# Der Connector-Sync nutzt denselben Lock exklusiv. Der Warmup hält ihn für
|
||||
# seinen gesamten Lauf nur lesend/geteilt. Dadurch kann der Placeholder- und
|
||||
# Shadow-Tree während Batch-Download, Piwigo-Aufruf und Restore nicht atomar
|
||||
# unter dem Warmup ausgetauscht werden.
|
||||
exec 9>"$LOCK_FILE"
|
||||
flock -s 9
|
||||
|
||||
COMMAND=("$@")
|
||||
|
||||
set +e
|
||||
"${COMMAND[@]}"
|
||||
result=$?
|
||||
set -e
|
||||
|
||||
# Ein Connector-Sync kann während eines bereits laufenden Warmups neue Alben
|
||||
# melden. Der Dispatcher legt dafür nur eine Prioritätsmarke an; er startet
|
||||
# keinen zweiten konkurrierenden Worker. Falls der aktive Lauf die Marke noch
|
||||
# nicht selbst übernehmen konnte, wird sie hier nach seinem sauberen Ende
|
||||
# garantiert nachgeholt. Dadurch geht ein Sofort-Warmup nie verloren.
|
||||
if [[ -f "$PRIORITY_FILE" ]]; then
|
||||
rm -f -- "$PRIORITY_FILE"
|
||||
SYNC_COMMAND=()
|
||||
replaced=0
|
||||
for arg in "${COMMAND[@]}"; do
|
||||
if [[ "$arg" == --mode=* ]]; then
|
||||
SYNC_COMMAND+=("--mode=sync")
|
||||
replaced=1
|
||||
else
|
||||
SYNC_COMMAND+=("$arg")
|
||||
fi
|
||||
done
|
||||
if [[ "$replaced" -eq 0 ]]; then
|
||||
SYNC_COMMAND+=("--mode=sync")
|
||||
fi
|
||||
|
||||
set +e
|
||||
"${SYNC_COMMAND[@]}"
|
||||
sync_result=$?
|
||||
set -e
|
||||
if [[ "$sync_result" -ne 0 ]]; then
|
||||
result="$sync_result"
|
||||
fi
|
||||
fi
|
||||
|
||||
exit "$result"
|
||||
41
runtime/lib/run-webdav-priority-wait.sh
Normal file
41
runtime/lib/run-webdav-priority-wait.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
STATE_DIR="${1:-}"
|
||||
shift || true
|
||||
|
||||
[[ -n "$STATE_DIR" ]] || { echo "Connector-State-Verzeichnis fehlt." >&2; exit 2; }
|
||||
[[ $# -gt 0 ]] || { echo "Warmup-Kommando fehlt." >&2; exit 2; }
|
||||
|
||||
PRIORITY_FILE="$STATE_DIR/webdav-cache-warmup-priority-sync"
|
||||
ATTEMPTS=0
|
||||
MAX_ATTEMPTS=3600
|
||||
|
||||
# Der Dispatcher setzt die Prioritätsmarke vor dem Start. Falls bereits ein
|
||||
# Warmup läuft, beendet der zusätzliche Worker sich wegen seines Prozess-Locks
|
||||
# sofort und lässt die Marke unangetastet. Wir probieren deshalb erst dann
|
||||
# erneut, wenn die Marke noch vorhanden ist. Sobald ein Sync-Worker den Lock
|
||||
# tatsächlich übernimmt, entfernt er die Marke selbst und arbeitet den neuen
|
||||
# Bestand ab. Dadurch geht die Sofort-Priorität nicht verloren und es gibt nie
|
||||
# zwei gleichzeitig arbeitende Warmup-Worker derselben Verbindung.
|
||||
while [[ -f "$PRIORITY_FILE" ]]; do
|
||||
ATTEMPTS=$((ATTEMPTS + 1))
|
||||
if [[ "$ATTEMPTS" -gt "$MAX_ATTEMPTS" ]]; then
|
||||
echo "Priorisierter WebDAV-Warmup wartet seit zu langer Zeit auf den aktiven Worker." >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
set +e
|
||||
"$@"
|
||||
result=$?
|
||||
set -e
|
||||
|
||||
if [[ "$result" -ne 0 ]]; then
|
||||
exit "$result"
|
||||
fi
|
||||
|
||||
[[ -f "$PRIORITY_FILE" ]] || exit 0
|
||||
sleep 1
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -61,6 +61,16 @@ function bratonien_tools_cache_warmup_status_file($connection_id)
|
||||
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-webdav-warmup.status-'.(int)$connection_id.'.json';
|
||||
}
|
||||
|
||||
function bratonien_tools_cache_warmup_priority_file($state_dir)
|
||||
{
|
||||
return rtrim((string)$state_dir, '/').'/webdav-cache-warmup-priority-sync';
|
||||
}
|
||||
|
||||
function bratonien_tools_cache_warmup_priority_pending($priority_file)
|
||||
{
|
||||
return is_string($priority_file) && $priority_file !== '' && is_file($priority_file);
|
||||
}
|
||||
|
||||
function bratonien_tools_cache_warmup_write_json($file, array $payload)
|
||||
{
|
||||
$directory = dirname($file);
|
||||
@@ -575,10 +585,16 @@ function bratonien_tools_cache_warmup_stage_pending(array $selected, array $stat
|
||||
return $pending;
|
||||
}
|
||||
|
||||
function bratonien_tools_cache_warmup_run_stage($connection_id, $stage, array $selected, array &$state, array $credentials, $batch_size)
|
||||
function bratonien_tools_cache_warmup_run_stage($connection_id, $stage, array $selected, array &$state, array $credentials, $batch_size, $priority_file='')
|
||||
{
|
||||
$pending = bratonien_tools_cache_warmup_stage_pending($selected, $state, $stage);
|
||||
if (!$pending) return array('ok'=>true, 'success'=>array(), 'failed'=>0);
|
||||
if (!$pending) return array('ok'=>true, 'success'=>array(), 'failed'=>0, 'preempted'=>false);
|
||||
|
||||
if ($stage === 2 && bratonien_tools_cache_warmup_priority_pending($priority_file))
|
||||
{
|
||||
bratonien_tools_cache_warmup_log('stage2_preempted', array('connection_id'=>$connection_id, 'point'=>'before_first_batch'));
|
||||
return array('ok'=>true, 'success'=>array(), 'failed'=>0, 'preempted'=>true);
|
||||
}
|
||||
|
||||
$temp_root = PHPWG_ROOT_PATH.'upload/bratonien-webdav-warmup';
|
||||
if (!is_dir($temp_root) && !@mkdir($temp_root, 0775, true) && !is_dir($temp_root)) throw new RuntimeException('Warmup-Temp-Verzeichnis konnte nicht angelegt werden.');
|
||||
@@ -652,8 +668,14 @@ function bratonien_tools_cache_warmup_run_stage($connection_id, $stage, array $s
|
||||
|
||||
foreach ($downloads as $file) @unlink($file);
|
||||
@rmdir($dir);
|
||||
|
||||
if ($stage === 2 && bratonien_tools_cache_warmup_priority_pending($priority_file))
|
||||
{
|
||||
bratonien_tools_cache_warmup_log('stage2_preempted', array('connection_id'=>$connection_id, 'point'=>'after_batch', 'batch'=>$batch_number));
|
||||
return array('ok'=>$failed === 0, 'success'=>$success, 'failed'=>$failed, 'preempted'=>true);
|
||||
}
|
||||
}
|
||||
return array('ok'=>$failed === 0, 'success'=>$success, 'failed'=>$failed);
|
||||
return array('ok'=>$failed === 0, 'success'=>$success, 'failed'=>$failed, 'preempted'=>false);
|
||||
}
|
||||
|
||||
function bratonien_tools_cache_warmup_run($connection_id, $mode)
|
||||
@@ -670,6 +692,7 @@ function bratonien_tools_cache_warmup_run($connection_id, $mode)
|
||||
$state_dir = rtrim((string)($credentials['config']['state_dir'] ?? ''), '/');
|
||||
if ($state_dir === '') throw new RuntimeException('Connector-State-Verzeichnis fehlt.');
|
||||
if (!is_dir($state_dir) && !@mkdir($state_dir, 0750, true) && !is_dir($state_dir)) throw new RuntimeException('Connector-State-Verzeichnis konnte nicht angelegt werden.');
|
||||
$priority_file = bratonien_tools_cache_warmup_priority_file($state_dir);
|
||||
|
||||
$process_lock = @fopen($state_dir.'/webdav-cache-warmup.lock', 'c');
|
||||
if (!$process_lock || !@flock($process_lock, LOCK_EX | LOCK_NB))
|
||||
@@ -681,6 +704,12 @@ function bratonien_tools_cache_warmup_run($connection_id, $mode)
|
||||
|
||||
try
|
||||
{
|
||||
// Nur der Sync-Worker, der den Prozess-Lock tatsächlich erhalten hat,
|
||||
// konsumiert seine eigene Prioritätsmarke. Ein zweiter Sync-Aufruf während
|
||||
// eines laufenden Workers lässt die Marke stehen, damit der aktive Lauf
|
||||
// Stufe 2 sicher zwischen zwei vollständigen Batches unterbrechen kann.
|
||||
if ($mode === 'sync' && is_file($priority_file)) @unlink($priority_file);
|
||||
|
||||
$scan = bratonien_tools_cache_warmup_scan($connection_id);
|
||||
$state = bratonien_tools_cache_warmup_load_state($connection_id);
|
||||
if ($state === null)
|
||||
@@ -723,14 +752,24 @@ function bratonien_tools_cache_warmup_run($connection_id, $mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
$stage1 = bratonien_tools_cache_warmup_run_stage($connection_id, 1, $selected, $state, $credentials, $settings['batch_size']);
|
||||
$stage1 = bratonien_tools_cache_warmup_run_stage($connection_id, 1, $selected, $state, $credentials, $settings['batch_size'], $priority_file);
|
||||
$stage2_candidates = array();
|
||||
foreach ($selected as $image_id=>$image)
|
||||
{
|
||||
$saved = isset($state['images'][(string)$image_id]) && is_array($state['images'][(string)$image_id]) ? $state['images'][(string)$image_id] : array();
|
||||
if (isset($saved['stage1_signature']) && hash_equals((string)$saved['stage1_signature'], $image['signature'])) $stage2_candidates[$image_id] = $image;
|
||||
}
|
||||
$stage2 = bratonien_tools_cache_warmup_run_stage($connection_id, 2, $stage2_candidates, $state, $credentials, $settings['batch_size']);
|
||||
$stage2 = bratonien_tools_cache_warmup_run_stage($connection_id, 2, $stage2_candidates, $state, $credentials, $settings['batch_size'], $priority_file);
|
||||
|
||||
if (!empty($stage2['preempted']))
|
||||
{
|
||||
bratonien_tools_cache_warmup_status($connection_id, 'preempted', 'Warmup Stufe 2 wurde nach einem vollständigen Batch für neue Sync-Priorität freigegeben.', array(
|
||||
'mode'=>$mode,
|
||||
'stage1_failed'=>(int)$stage1['failed'],
|
||||
'stage2_failed'=>(int)$stage2['failed'],
|
||||
));
|
||||
return ((int)$stage1['failed'] + (int)$stage2['failed']) > 0 ? 2 : 0;
|
||||
}
|
||||
|
||||
foreach ($scan['albums'] as $album_id) $state['albums'][(string)$album_id] = array('seen_at'=>time());
|
||||
if ($mode === 'periodic' || $mode === 'manual') $state['last_periodic_at'] = time();
|
||||
|
||||
@@ -5,7 +5,7 @@ if (PHP_SAPI !== 'cli')
|
||||
exit;
|
||||
}
|
||||
|
||||
$piwigo_root = realpath(dirname(__DIR__, 3));
|
||||
$piwigo_root = realpath(dirname(__DIR__, 4));
|
||||
if ($piwigo_root === false)
|
||||
{
|
||||
fwrite(STDERR, "Piwigo-Root wurde nicht gefunden.\n");
|
||||
|
||||
@@ -5,7 +5,7 @@ if (PHP_SAPI !== 'cli')
|
||||
exit;
|
||||
}
|
||||
|
||||
$piwigo_root = realpath(dirname(__DIR__, 2));
|
||||
$piwigo_root = realpath(dirname(__DIR__, 3));
|
||||
if ($piwigo_root === false)
|
||||
{
|
||||
fwrite(STDERR, "Piwigo-Root wurde nicht gefunden.\n");
|
||||
@@ -58,15 +58,15 @@ if (!function_exists('exec'))
|
||||
}
|
||||
|
||||
$worker = realpath(BRATONIEN_TOOLS_PATH.'runtime/lib/webdav-cache-warmup.php');
|
||||
$guard = realpath(BRATONIEN_TOOLS_PATH.'runtime/lib/run-webdav-cache-warmup.sh');
|
||||
$priority_waiter = realpath(BRATONIEN_TOOLS_PATH.'runtime/lib/run-webdav-priority-wait.sh');
|
||||
if (!$worker || !is_file($worker))
|
||||
{
|
||||
fwrite(STDERR, "WebDAV-Cache-Warmup-Worker wurde nicht gefunden.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!$guard || !is_file($guard))
|
||||
if (!$priority_waiter || !is_file($priority_waiter))
|
||||
{
|
||||
fwrite(STDERR, "WebDAV-Cache-Warmup-Schutzskript wurde nicht gefunden.\n");
|
||||
fwrite(STDERR, "WebDAV-Prioritäts-Warter wurde nicht gefunden.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -135,14 +135,22 @@ foreach (bratonien_tools_nc_connector_connections() as $connection)
|
||||
@chmod($priority_file, 0664);
|
||||
}
|
||||
|
||||
// Der Guard hält webdav-sync.lock für den gesamten Worker-Lauf geteilt.
|
||||
// Dadurch kann der produktive Source-/Shadow-Tree während Download, Swap,
|
||||
// Piwigo-Aufruf und Restore nicht von einem Connector-Sync ersetzt werden.
|
||||
$base = escapeshellarg('/bin/bash').' '.escapeshellarg($guard).' '.escapeshellarg($state_dir)
|
||||
.' '.escapeshellarg(PHP_BINARY).' '.escapeshellarg($worker)
|
||||
$worker_command = escapeshellarg(PHP_BINARY).' '.escapeshellarg($worker)
|
||||
.' --connection-id='.$connection_id
|
||||
.' --mode='.escapeshellarg($mode);
|
||||
|
||||
if ($mode === 'sync')
|
||||
{
|
||||
// Ein bereits laufender Worker darf seinen aktuellen Bild-/Batchvorgang
|
||||
// vollständig restaurieren. Der Waiter startet den priorisierten Sync-Lauf
|
||||
// danach automatisch, statt die Prioritätsmarke verloren gehen zu lassen.
|
||||
$base = escapeshellarg('/bin/bash').' '.escapeshellarg($priority_waiter).' '.escapeshellarg($state_dir).' '.$worker_command;
|
||||
}
|
||||
else
|
||||
{
|
||||
$base = $worker_command;
|
||||
}
|
||||
|
||||
if ($wait)
|
||||
{
|
||||
$output = array();
|
||||
|
||||
70
webdav-derivative-guard.php
Normal file
70
webdav-derivative-guard.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
define('PHPWG_ROOT_PATH', '../../');
|
||||
include_once(PHPWG_ROOT_PATH.'include/common.inc.php');
|
||||
require_once(PHPWG_ROOT_PATH.'include/derivative.inc.php');
|
||||
|
||||
if (!defined('BRATONIEN_TOOLS_PATH'))
|
||||
{
|
||||
define('BRATONIEN_TOOLS_ID', basename(__DIR__));
|
||||
define('BRATONIEN_TOOLS_PATH', PHPWG_ROOT_PATH.'plugins/'.BRATONIEN_TOOLS_ID.'/');
|
||||
}
|
||||
require_once(BRATONIEN_TOOLS_PATH.'include/webdav_materialize_runtime.inc.php');
|
||||
|
||||
$image_id = (int)($_GET['id'] ?? 0);
|
||||
if ($image_id < 1)
|
||||
{
|
||||
http_response_code(400);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo 'Ungueltige Derivatanforderung.';
|
||||
exit;
|
||||
}
|
||||
|
||||
$source = bratonien_tools_webdav_materialize_source_info($image_id);
|
||||
if (!$source)
|
||||
{
|
||||
http_response_code(404);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo 'WebDAV-Bildquelle nicht gefunden.';
|
||||
exit;
|
||||
}
|
||||
|
||||
$state_dir = rtrim((string)($source['state_dir'] ?? ''), '/');
|
||||
if ($state_dir === '')
|
||||
{
|
||||
http_response_code(503);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo 'Connector-State-Verzeichnis fehlt.';
|
||||
exit;
|
||||
}
|
||||
|
||||
$lock = @fopen($state_dir.'/webdav-sync.lock', 'c');
|
||||
if (!$lock)
|
||||
{
|
||||
http_response_code(503);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo 'Connector-Sync-Lock konnte nicht geoeffnet werden.';
|
||||
exit;
|
||||
}
|
||||
|
||||
// On-Demand bleibt parallel zu anderen Bildabrufen moeglich, verhindert aber
|
||||
// fuer die Dauer dieses einzelnen Derivataufrufs einen exklusiven Connector-
|
||||
// Tree-Swap. Der bestehende Bild-Lock im eigentlichen Endpoint koordiniert
|
||||
// weiterhin On-Demand und Warmup fuer dieselbe Bild-ID.
|
||||
if (!@flock($lock, LOCK_SH))
|
||||
{
|
||||
fclose($lock);
|
||||
http_response_code(503);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo 'Connector-Sync-Schutz konnte nicht gesetzt werden.';
|
||||
exit;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
include BRATONIEN_TOOLS_PATH.'webdav-derivative.php';
|
||||
}
|
||||
finally
|
||||
{
|
||||
@flock($lock, LOCK_UN);
|
||||
fclose($lock);
|
||||
}
|
||||
Reference in New Issue
Block a user