mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-10-26 07:46:31 +01:00
more improvements to safe upgrade
This commit is contained in:
@@ -290,6 +290,7 @@ class SafeUpgradeManager
|
|||||||
$result = [
|
$result = [
|
||||||
'job' => $manifest ?: null,
|
'job' => $manifest ?: null,
|
||||||
'progress' => $progress,
|
'progress' => $progress,
|
||||||
|
'context' => $this->buildStatusContext(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->clearJobContext();
|
$this->clearJobContext();
|
||||||
@@ -417,6 +418,7 @@ class SafeUpgradeManager
|
|||||||
'log' => $logPath,
|
'log' => $logPath,
|
||||||
'progress' => $this->getProgress(),
|
'progress' => $this->getProgress(),
|
||||||
'job' => $this->readManifest(),
|
'job' => $this->readManifest(),
|
||||||
|
'context' => $this->buildStatusContext(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,6 +555,7 @@ class SafeUpgradeManager
|
|||||||
'status' => 'noop',
|
'status' => 'noop',
|
||||||
'version' => $localVersion,
|
'version' => $localVersion,
|
||||||
'message' => 'Grav is already up to date.',
|
'message' => 'Grav is already up to date.',
|
||||||
|
'context' => $this->buildStatusContext(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,6 +650,7 @@ class SafeUpgradeManager
|
|||||||
'version' => $remoteVersion,
|
'version' => $remoteVersion,
|
||||||
'manifest' => $manifest,
|
'manifest' => $manifest,
|
||||||
'previous_version' => $localVersion,
|
'previous_version' => $localVersion,
|
||||||
|
'context' => $this->buildStatusContext(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,6 +900,7 @@ class SafeUpgradeManager
|
|||||||
'status' => 'finalized',
|
'status' => 'finalized',
|
||||||
'version' => $localVersion,
|
'version' => $localVersion,
|
||||||
'message' => 'Post-install scripts completed.',
|
'message' => 'Post-install scripts completed.',
|
||||||
|
'context' => $this->buildStatusContext(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1011,9 +1016,31 @@ class SafeUpgradeManager
|
|||||||
return [
|
return [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
|
'context' => $this->buildStatusContext(),
|
||||||
] + $extra;
|
] + $extra;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function buildStatusContext(): ?string
|
||||||
|
{
|
||||||
|
$context = [];
|
||||||
|
|
||||||
|
if ($this->jobManifestPath) {
|
||||||
|
$context['manifest'] = $this->jobManifestPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->progressPath) {
|
||||||
|
$context['progress'] = $this->progressPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$encoded = json_encode($context);
|
||||||
|
|
||||||
|
return $encoded === false ? null : base64_encode($encoded);
|
||||||
|
}
|
||||||
|
|
||||||
protected function ensureExecutablePermissions(): void
|
protected function ensureExecutablePermissions(): void
|
||||||
{
|
{
|
||||||
$executables = [
|
$executables = [
|
||||||
|
|||||||
@@ -33,11 +33,73 @@ $readJson = static function (string $path): ?array {
|
|||||||
|
|
||||||
$progress = null;
|
$progress = null;
|
||||||
$manifest = null;
|
$manifest = null;
|
||||||
|
$manifestPath = null;
|
||||||
|
$progressPath = null;
|
||||||
|
|
||||||
|
$normalizeDir = static function (string $path): string {
|
||||||
|
$normalized = str_replace('\\', '/', $path);
|
||||||
|
|
||||||
|
return rtrim($normalized, '/');
|
||||||
|
};
|
||||||
|
|
||||||
|
$jobsDirNormalized = $normalizeDir($jobsDir);
|
||||||
|
$userDataDirNormalized = $normalizeDir(dirname($jobsDir));
|
||||||
|
|
||||||
|
$contextParam = $_GET['context'] ?? '';
|
||||||
|
if ($contextParam !== '') {
|
||||||
|
$decodedRaw = base64_decode(strtr($contextParam, ' ', '+'), true);
|
||||||
|
if ($decodedRaw !== false) {
|
||||||
|
$decoded = json_decode($decodedRaw, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$validatePath = static function (string $candidate) use ($normalizeDir, $jobsDirNormalized, $userDataDirNormalized) {
|
||||||
|
$candidate = str_replace('\\', '/', $candidate);
|
||||||
|
$directory = dirname($candidate);
|
||||||
|
$real = realpath($directory);
|
||||||
|
if ($real === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$real = $normalizeDir($real);
|
||||||
|
if (strpos($real, $jobsDirNormalized) !== 0 && strpos($real, $userDataDirNormalized) !== 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $candidate;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!empty($decoded['manifest'])) {
|
||||||
|
$candidate = $validatePath((string)$decoded['manifest']);
|
||||||
|
if ($candidate) {
|
||||||
|
$manifestPath = $candidate;
|
||||||
|
if (is_file($candidate)) {
|
||||||
|
$manifest = $readJson($candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($decoded['progress'])) {
|
||||||
|
$candidate = $validatePath((string)$decoded['progress']);
|
||||||
|
if ($candidate) {
|
||||||
|
$progressPath = $candidate;
|
||||||
|
if (is_file($candidate)) {
|
||||||
|
$progress = $readJson($candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($jobId !== '') {
|
if ($jobId !== '') {
|
||||||
$jobPath = $jobsDir . '/' . $jobId;
|
$jobPath = $jobsDir . '/' . $jobId;
|
||||||
$progress = $readJson($jobPath . '/progress.json');
|
$progressPath = $progressPath ?: ($jobPath . '/progress.json');
|
||||||
$manifest = $readJson($jobPath . '/manifest.json');
|
$manifestPath = $manifestPath ?: ($jobPath . '/manifest.json');
|
||||||
|
if (is_file($progressPath)) {
|
||||||
|
$progress = $readJson($progressPath);
|
||||||
|
}
|
||||||
|
if (is_file($manifestPath)) {
|
||||||
|
$manifest = $readJson($manifestPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (!$progress && !$manifest && !is_dir($jobPath)) {
|
if (!$progress && !$manifest && !is_dir($jobPath)) {
|
||||||
$progress = $readJson($fallbackProgress) ?: [
|
$progress = $readJson($fallbackProgress) ?: [
|
||||||
@@ -46,37 +108,45 @@ if ($jobId !== '') {
|
|||||||
'percent' => null,
|
'percent' => null,
|
||||||
'timestamp' => time(),
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
'status' => 'success',
|
|
||||||
'message' => 'Safe upgrade job not found.',
|
|
||||||
'data' => [
|
|
||||||
'job' => null,
|
|
||||||
'progress' => $progress,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($progress === null) {
|
if ($progress === null) {
|
||||||
$progress = $readJson($fallbackProgress) ?: [
|
if ($progressPath && is_file($progressPath)) {
|
||||||
'stage' => 'idle',
|
$progress = $readJson($progressPath);
|
||||||
'message' => '',
|
}
|
||||||
'percent' => null,
|
|
||||||
'timestamp' => time(),
|
if ($progress === null) {
|
||||||
];
|
$progress = $readJson($fallbackProgress) ?: [
|
||||||
|
'stage' => 'idle',
|
||||||
|
'message' => '',
|
||||||
|
'percent' => null,
|
||||||
|
'timestamp' => time(),
|
||||||
|
];
|
||||||
|
$progressPath = $fallbackProgress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($jobId !== '' && is_array($progress) && !isset($progress['job_id'])) {
|
if ($jobId !== '' && is_array($progress) && !isset($progress['job_id'])) {
|
||||||
$progress['job_id'] = $jobId;
|
$progress['job_id'] = $jobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$contextPayload = [];
|
||||||
|
if ($manifestPath) {
|
||||||
|
$contextPayload['manifest'] = $manifestPath;
|
||||||
|
}
|
||||||
|
if ($progressPath) {
|
||||||
|
$contextPayload['progress'] = $progressPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
$contextToken = $contextPayload ? base64_encode(json_encode($contextPayload)) : null;
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'data' => [
|
'data' => [
|
||||||
'job' => $manifest ?: null,
|
'job' => $manifest ?: null,
|
||||||
'progress' => $progress,
|
'progress' => $progress,
|
||||||
|
'context' => $contextToken,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export default class SafeUpgrade {
|
|||||||
this.active = false;
|
this.active = false;
|
||||||
this.jobId = null;
|
this.jobId = null;
|
||||||
this.statusFailures = 0;
|
this.statusFailures = 0;
|
||||||
|
this.statusContext = null;
|
||||||
this.directStatusUrl = this.resolveDirectStatusUrl();
|
this.directStatusUrl = this.resolveDirectStatusUrl();
|
||||||
this.preferDirectStatus = !!this.directStatusUrl;
|
this.preferDirectStatus = !!this.directStatusUrl;
|
||||||
|
|
||||||
@@ -120,6 +121,7 @@ export default class SafeUpgrade {
|
|||||||
this.decisions = {};
|
this.decisions = {};
|
||||||
this.statusFailures = 0;
|
this.statusFailures = 0;
|
||||||
this.preferDirectStatus = !!this.directStatusUrl;
|
this.preferDirectStatus = !!this.directStatusUrl;
|
||||||
|
this.statusContext = null;
|
||||||
this.renderLoading();
|
this.renderLoading();
|
||||||
this.modal.open();
|
this.modal.open();
|
||||||
this.fetchPreflight();
|
this.fetchPreflight();
|
||||||
@@ -412,6 +414,7 @@ export default class SafeUpgrade {
|
|||||||
}
|
}
|
||||||
this.statusFailures = 0;
|
this.statusFailures = 0;
|
||||||
this.preferDirectStatus = !!this.directStatusUrl;
|
this.preferDirectStatus = !!this.directStatusUrl;
|
||||||
|
this.statusContext = data.context || null;
|
||||||
this.beginPolling(1200);
|
this.beginPolling(1200);
|
||||||
} else {
|
} else {
|
||||||
this.renderResult(data);
|
this.renderResult(data);
|
||||||
@@ -453,8 +456,18 @@ export default class SafeUpgrade {
|
|||||||
resolveStatusEndpoint() {
|
resolveStatusEndpoint() {
|
||||||
const useDirect = this.directStatusUrl && this.preferDirectStatus;
|
const useDirect = this.directStatusUrl && this.preferDirectStatus;
|
||||||
let url = useDirect ? this.directStatusUrl : this.urls.status;
|
let url = useDirect ? this.directStatusUrl : this.urls.status;
|
||||||
|
const params = [];
|
||||||
|
|
||||||
if (this.jobId) {
|
if (this.jobId) {
|
||||||
url += (url.includes('?') ? '&' : '?') + `job=${encodeURIComponent(this.jobId)}`;
|
params.push(`job=${encodeURIComponent(this.jobId)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.statusContext) {
|
||||||
|
params.push(`context=${encodeURIComponent(this.statusContext)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.length) {
|
||||||
|
url += (url.includes('?') ? '&' : '?') + params.join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -532,6 +545,9 @@ export default class SafeUpgrade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const payload = response.data || {};
|
const payload = response.data || {};
|
||||||
|
if (Object.prototype.hasOwnProperty.call(payload, 'context')) {
|
||||||
|
this.statusContext = payload.context || null;
|
||||||
|
}
|
||||||
const job = payload.job || {};
|
const job = payload.job || {};
|
||||||
const data = payload.progress || payload;
|
const data = payload.progress || payload;
|
||||||
nextStage = data.stage || null;
|
nextStage = data.stage || null;
|
||||||
|
|||||||
15
themes/grav/js/admin.min.js
vendored
15
themes/grav/js/admin.min.js
vendored
@@ -4610,6 +4610,7 @@ var SafeUpgrade = /*#__PURE__*/function () {
|
|||||||
this.active = false;
|
this.active = false;
|
||||||
this.jobId = null;
|
this.jobId = null;
|
||||||
this.statusFailures = 0;
|
this.statusFailures = 0;
|
||||||
|
this.statusContext = null;
|
||||||
this.directStatusUrl = this.resolveDirectStatusUrl();
|
this.directStatusUrl = this.resolveDirectStatusUrl();
|
||||||
this.preferDirectStatus = !!this.directStatusUrl;
|
this.preferDirectStatus = !!this.directStatusUrl;
|
||||||
this.registerEvents();
|
this.registerEvents();
|
||||||
@@ -4676,6 +4677,7 @@ var SafeUpgrade = /*#__PURE__*/function () {
|
|||||||
this.decisions = {};
|
this.decisions = {};
|
||||||
this.statusFailures = 0;
|
this.statusFailures = 0;
|
||||||
this.preferDirectStatus = !!this.directStatusUrl;
|
this.preferDirectStatus = !!this.directStatusUrl;
|
||||||
|
this.statusContext = null;
|
||||||
this.renderLoading();
|
this.renderLoading();
|
||||||
this.modal.open();
|
this.modal.open();
|
||||||
this.fetchPreflight();
|
this.fetchPreflight();
|
||||||
@@ -4882,6 +4884,7 @@ var SafeUpgrade = /*#__PURE__*/function () {
|
|||||||
}
|
}
|
||||||
_this4.statusFailures = 0;
|
_this4.statusFailures = 0;
|
||||||
_this4.preferDirectStatus = !!_this4.directStatusUrl;
|
_this4.preferDirectStatus = !!_this4.directStatusUrl;
|
||||||
|
_this4.statusContext = data.context || null;
|
||||||
_this4.beginPolling(1200);
|
_this4.beginPolling(1200);
|
||||||
} else {
|
} else {
|
||||||
_this4.renderResult(data);
|
_this4.renderResult(data);
|
||||||
@@ -4921,8 +4924,15 @@ var SafeUpgrade = /*#__PURE__*/function () {
|
|||||||
value: function resolveStatusEndpoint() {
|
value: function resolveStatusEndpoint() {
|
||||||
var useDirect = this.directStatusUrl && this.preferDirectStatus;
|
var useDirect = this.directStatusUrl && this.preferDirectStatus;
|
||||||
var url = useDirect ? this.directStatusUrl : this.urls.status;
|
var url = useDirect ? this.directStatusUrl : this.urls.status;
|
||||||
|
var params = [];
|
||||||
if (this.jobId) {
|
if (this.jobId) {
|
||||||
url += (url.indexOf('?') !== -1 ? '&' : '?') + "job=".concat(encodeURIComponent(this.jobId));
|
params.push("job=".concat(encodeURIComponent(this.jobId)));
|
||||||
|
}
|
||||||
|
if (this.statusContext) {
|
||||||
|
params.push("context=".concat(encodeURIComponent(this.statusContext)));
|
||||||
|
}
|
||||||
|
if (params.length) {
|
||||||
|
url += (url.indexOf('?') !== -1 ? '&' : '?') + params.join('&');
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
url: url,
|
url: url,
|
||||||
@@ -5002,6 +5012,9 @@ var SafeUpgrade = /*#__PURE__*/function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var payload = response.data || {};
|
var payload = response.data || {};
|
||||||
|
if (Object.prototype.hasOwnProperty.call(payload, 'context')) {
|
||||||
|
_this6.statusContext = payload.context || null;
|
||||||
|
}
|
||||||
var job = payload.job || {};
|
var job = payload.job || {};
|
||||||
var data = payload.progress || payload;
|
var data = payload.progress || payload;
|
||||||
nextStage = data.stage || null;
|
nextStage = data.stage || null;
|
||||||
|
|||||||
Reference in New Issue
Block a user