Updates: Allow automatic display of keys/Copy button ack
This commit is contained in:
@@ -1,17 +1,26 @@
|
||||
<?php
|
||||
/** @var \MageSail\Magesail\Block\Adminhtml\Tunnel $block */
|
||||
$status = $block->getTunnelStatus();
|
||||
$formKey = $block->getFormKey();
|
||||
$statusUrl = $block->escapeUrl($block->getStatusUrl());
|
||||
$formKey = $block->escapeHtml($block->getFormKey());
|
||||
$statusUrl = $block->getStatusUrl();
|
||||
$indexUrl = $block->getIndexUrl();
|
||||
$configUrl = $block->escapeUrl($block->getConfigUrl());
|
||||
$logPath = $block->escapeHtml($block->getLogFilePath());
|
||||
$logLines = $block->getRecentLogLines();
|
||||
$running = $status['running'];
|
||||
?>
|
||||
<div class="magesail-tunnel">
|
||||
<div class="magesail-tunnel"
|
||||
id="magesail-root"
|
||||
data-status-url="<?= $block->escapeUrl($statusUrl) ?>"
|
||||
data-index-url="<?= $block->escapeUrl($indexUrl) ?>"
|
||||
data-logtail-url="<?= $block->escapeUrl($block->getLogtailUrl()) ?>"
|
||||
data-form-key="<?= $formKey ?>">
|
||||
<div class="page-title-wrapper">
|
||||
<h1 class="page-title"><?= $block->escapeHtml(__('Holesail Tunnel Management')) ?></h1>
|
||||
</div>
|
||||
|
||||
<div id="magesail-flash" class="message" style="display:none"></div>
|
||||
|
||||
<div class="admin__page-section">
|
||||
<div class="admin__page-section-title">
|
||||
<span class="title"><?= $block->escapeHtml(__('Tunnel status')) ?></span>
|
||||
@@ -23,104 +32,46 @@ $logLines = $block->getRecentLogLines();
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<?php if ($status['running']): ?>
|
||||
<div class="message message-success success" id="magesail-status-banner">
|
||||
<span id="tunnel-status-text">
|
||||
<?= $block->escapeHtml(__('Tunnel is running (PID: %1).', $status['pid'])) ?>
|
||||
</span>
|
||||
<div id="magesail-stopped" class="magesail-panel" style="<?= $running ? 'display:none' : '' ?>">
|
||||
<div class="message message-warning warning">
|
||||
<span><?= $block->escapeHtml(__('No active tunnel. Start one to expose your dev site.')) ?></span>
|
||||
</div>
|
||||
<div class="magesail-actions">
|
||||
<button type="button" class="action-primary" id="magesail-btn-start">
|
||||
<span><?= $block->escapeHtml(__('Start Tunnel')) ?></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="magesail-running" class="magesail-panel" style="<?= $running ? '' : 'display:none' ?>">
|
||||
<div class="message message-success success" id="magesail-status-banner">
|
||||
<span id="tunnel-status-text"><?php
|
||||
if ($running && $status['pid']) {
|
||||
echo $block->escapeHtml(__('Tunnel is running (PID: %1).', $status['pid']));
|
||||
}
|
||||
?></span>
|
||||
</div>
|
||||
<div class="magesail-key-wrap" id="magesail-key-section">
|
||||
<strong><?= $block->escapeHtml(__('Share key')) ?></strong>
|
||||
<code class="magesail-key" id="magesail-key-value"><?= $block->escapeHtml($status['key'] ?: '') ?></code>
|
||||
<span class="magesail-generating" id="magesail-key-loading" style="<?= $status['key'] ? 'display:none' : '' ?>">
|
||||
<span class="magesail-generating" id="magesail-key-loading" style="<?= ($running && !$status['key']) ? '' : 'display:none' ?>">
|
||||
<?= $block->escapeHtml(__('Generating key…')) ?>
|
||||
</span>
|
||||
<button type="button" class="action-default" id="magesail-copy-key" <?= $status['key'] ? '' : 'disabled' ?>>
|
||||
<button type="button" class="action-default" id="magesail-copy-key" data-original-label="<?= $block->escapeHtmlAttr(__('Copy key')) ?>" <?= ($status['key'] ?? '') ? '' : 'disabled' ?>>
|
||||
<span><?= $block->escapeHtml(__('Copy key')) ?></span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="admin__field-note">
|
||||
<?= $block->escapeHtml(__('Client example:')) ?>
|
||||
<code id="magesail-client-cmd"><?= $block->escapeHtml($status['key'] ? 'holesail ' . $status['key'] : 'holesail hs://…') ?></code>
|
||||
<button type="button" class="action-link" id="magesail-copy-cmd" <?= $status['key'] ? '' : 'disabled' ?>><?= $block->escapeHtml(__('Copy')) ?></button>
|
||||
<button type="button" class="action-link" id="magesail-copy-cmd" data-original-label="<?= $block->escapeHtmlAttr(__('Copy')) ?>"><?= $block->escapeHtml(__('Copy')) ?></button>
|
||||
</p>
|
||||
|
||||
<form method="post" action="<?= $block->escapeUrl($block->getUrl('*/*/index')) ?>" class="magesail-actions">
|
||||
<?= $block->getBlockHtml('formkey') ?>
|
||||
<input type="hidden" name="action" value="stop"/>
|
||||
<button type="submit" class="action-primary action-stop">
|
||||
<div class="magesail-actions">
|
||||
<button type="button" class="action-primary action-stop" id="magesail-btn-stop">
|
||||
<span><?= $block->escapeHtml(__('Stop Tunnel')) ?></span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
require(['jquery'], function ($) {
|
||||
var statusUrl = <?= json_encode($statusUrl) ?>;
|
||||
var pollTimer = null;
|
||||
var pollIntervalMs = 3000;
|
||||
|
||||
function copyText(text) {
|
||||
if (!text) return;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text);
|
||||
} else {
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
try { document.execCommand('copy'); } catch (e) {}
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
}
|
||||
|
||||
$('#magesail-copy-key').on('click', function () {
|
||||
copyText($('#magesail-key-value').text().trim());
|
||||
});
|
||||
$('#magesail-copy-cmd').on('click', function () {
|
||||
copyText($('#magesail-client-cmd').text().trim());
|
||||
});
|
||||
|
||||
function applyStatus(data) {
|
||||
if (!data || data.error) return;
|
||||
if (data.running && data.pid) {
|
||||
$('#tunnel-status-text').text(
|
||||
<?= json_encode((string) __('Tunnel is running (PID: %1).', '{{pid}}')) ?>.replace('{{pid}}', data.pid)
|
||||
);
|
||||
}
|
||||
if (data.key && String(data.key).trim() !== '') {
|
||||
var k = String(data.key).trim();
|
||||
$('#magesail-key-value').text(k);
|
||||
$('#magesail-client-cmd').text('holesail ' + k);
|
||||
$('#magesail-key-loading').hide();
|
||||
$('#magesail-copy-key, #magesail-copy-cmd').prop('disabled', false);
|
||||
if (pollTimer) {
|
||||
clearInterval(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var keyReady = <?= $status['key'] ? 'true' : 'false' ?>;
|
||||
if (!keyReady) {
|
||||
pollTimer = setInterval(function () {
|
||||
$.getJSON(statusUrl).done(applyStatus).fail(function () {});
|
||||
}, pollIntervalMs);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php else: ?>
|
||||
<div class="message message-warning warning">
|
||||
<span><?= $block->escapeHtml(__('No active tunnel. Start one to expose your dev site.')) ?></span>
|
||||
</div>
|
||||
<form method="post" action="<?= $block->escapeUrl($block->getUrl('*/*/index')) ?>" class="magesail-actions">
|
||||
<?= $block->getBlockHtml('formkey') ?>
|
||||
<input type="hidden" name="action" value="start"/>
|
||||
<button type="submit" class="action-primary action-start">
|
||||
<span><?= $block->escapeHtml(__('Start Tunnel')) ?></span>
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -129,15 +80,204 @@ $logLines = $block->getRecentLogLines();
|
||||
<span class="title"><?= $block->escapeHtml(__('Log tail (%1)', $logPath)) ?></span>
|
||||
</div>
|
||||
<div class="admin__page-section-content">
|
||||
<?php if ($logLines): ?>
|
||||
<pre class="magesail-log-preview" aria-label="<?= $block->escapeHtmlAttr(__('Recent log lines')) ?>"><?php
|
||||
<pre class="magesail-log-preview" id="magesail-log-preview" aria-label="<?= $block->escapeHtmlAttr(__('Recent log lines')) ?>"><?php
|
||||
if ($logLines) {
|
||||
foreach ($logLines as $line) {
|
||||
echo $block->escapeHtml($line) . "\n";
|
||||
}
|
||||
?></pre>
|
||||
<?php else: ?>
|
||||
<p class="admin__field-note"><?= $block->escapeHtml(__('No log output yet or log file not readable.')) ?></p>
|
||||
<?php endif; ?>
|
||||
} else {
|
||||
echo $block->escapeHtml(__('No log output yet or log file not readable.'));
|
||||
}
|
||||
?></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
require(['jquery', 'domReady!'], function ($) {
|
||||
var root = $('#magesail-root');
|
||||
var statusUrl = root.data('status-url');
|
||||
var indexUrl = root.data('index-url');
|
||||
var formKey = root.data('form-key');
|
||||
var pollTimer = null;
|
||||
var logPollTimer = null;
|
||||
var pidMsgTemplate = <?= json_encode((string) __('Tunnel is running (PID: %1).', '%1')) ?>;
|
||||
var copiedLabel = <?= json_encode((string) __('Copied!')) ?>;
|
||||
|
||||
function flash(msg, isError) {
|
||||
var el = $('#magesail-flash');
|
||||
el.removeClass('message-success success message-error error message-warning warning')
|
||||
.addClass(isError ? 'message-error error' : 'message-success success')
|
||||
.html('<span>' + $('<div/>').text(msg).html() + '</span>')
|
||||
.show();
|
||||
if (!isError) {
|
||||
setTimeout(function () { el.fadeOut(); }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
function copyText(text) {
|
||||
if (!text) return false;
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text);
|
||||
} else {
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function copyFeedback(btn) {
|
||||
var span = btn.find('span');
|
||||
if (!span.length) span = btn;
|
||||
var original = btn.data('original-label') || span.text();
|
||||
btn.addClass('magesail-copied');
|
||||
span.text(copiedLabel);
|
||||
setTimeout(function () {
|
||||
btn.removeClass('magesail-copied');
|
||||
span.text(original);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function applyStatus(data) {
|
||||
if (!data || typeof data.running === 'undefined') return;
|
||||
if (data.running && data.pid) {
|
||||
$('#tunnel-status-text').text(pidMsgTemplate.replace('%1', data.pid));
|
||||
}
|
||||
if (data.key && String(data.key).trim() !== '') {
|
||||
var k = String(data.key).trim();
|
||||
$('#magesail-key-value').text(k);
|
||||
$('#magesail-client-cmd').text('holesail ' + k);
|
||||
$('#magesail-key-loading').hide();
|
||||
$('#magesail-copy-key, #magesail-copy-cmd').prop('disabled', false);
|
||||
stopPoll();
|
||||
}
|
||||
}
|
||||
|
||||
function stopPoll() {
|
||||
if (pollTimer) {
|
||||
clearInterval(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function startPoll() {
|
||||
stopPoll();
|
||||
function tick() {
|
||||
$.ajax({
|
||||
url: statusUrl,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
xhrFields: { withCredentials: true }
|
||||
}).done(applyStatus).fail(function (xhr) {
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
flash(xhr.responseJSON.message, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
tick();
|
||||
pollTimer = setInterval(tick, 2000);
|
||||
}
|
||||
|
||||
function showStopped() {
|
||||
$('#magesail-running').hide();
|
||||
$('#magesail-stopped').show();
|
||||
stopPoll();
|
||||
$('#magesail-key-value').text('');
|
||||
$('#magesail-client-cmd').text('holesail hs://…');
|
||||
$('#magesail-key-loading').show();
|
||||
$('#magesail-copy-key, #magesail-copy-cmd').prop('disabled', true);
|
||||
}
|
||||
|
||||
function showRunning(status) {
|
||||
$('#magesail-stopped').hide();
|
||||
$('#magesail-running').show();
|
||||
applyStatus(status || { running: true });
|
||||
var hasKey = status && status.key && String(status.key).trim() !== '';
|
||||
if (!hasKey) {
|
||||
$('#magesail-key-loading').show();
|
||||
$('#magesail-copy-key, #magesail-copy-cmd').prop('disabled', true);
|
||||
startPoll();
|
||||
} else {
|
||||
$('#magesail-key-loading').hide();
|
||||
stopPoll();
|
||||
}
|
||||
}
|
||||
|
||||
$('#magesail-copy-key').on('click', function () {
|
||||
var text = $('#magesail-key-value').text().trim();
|
||||
if (copyText(text)) {
|
||||
copyFeedback($(this));
|
||||
}
|
||||
});
|
||||
$('#magesail-copy-cmd').on('click', function () {
|
||||
var text = $('#magesail-client-cmd').text().trim();
|
||||
if (copyText(text)) {
|
||||
copyFeedback($(this));
|
||||
}
|
||||
});
|
||||
|
||||
$('#magesail-btn-start').on('click', function () {
|
||||
var btn = $(this).prop('disabled', true);
|
||||
$.ajax({
|
||||
url: indexUrl,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
data: { form_key: formKey, action: 'start', ajax: '1' }
|
||||
}).done(function (res) {
|
||||
if (res.success && res.status) {
|
||||
flash(res.message || '', false);
|
||||
showRunning(res.status);
|
||||
} else {
|
||||
flash(res.message || 'Start failed', true);
|
||||
}
|
||||
}).fail(function (xhr) {
|
||||
var m = (xhr.responseJSON && xhr.responseJSON.message) ? xhr.responseJSON.message : 'Request failed';
|
||||
flash(m, true);
|
||||
}).always(function () {
|
||||
btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#magesail-btn-stop').on('click', function () {
|
||||
var btn = $(this).prop('disabled', true);
|
||||
$.ajax({
|
||||
url: indexUrl,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
data: { form_key: formKey, action: 'stop', ajax: '1' }
|
||||
}).done(function (res) {
|
||||
flash(res.message || '', false);
|
||||
showStopped();
|
||||
}).fail(function () {
|
||||
flash('Stop failed', true);
|
||||
}).always(function () {
|
||||
btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
if ($('#magesail-running').is(':visible') && !$('#magesail-key-value').text().trim()) {
|
||||
startPoll();
|
||||
}
|
||||
|
||||
var logUrl = root.data('logtail-url');
|
||||
if (logUrl) {
|
||||
logPollTimer = setInterval(function () {
|
||||
$.getJSON(logUrl).done(function (res) {
|
||||
if (res.lines && res.lines.length) {
|
||||
$('#magesail-log-preview').text(res.lines.join('\n'));
|
||||
}
|
||||
});
|
||||
}, 4000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -58,3 +58,11 @@
|
||||
.magesail-tunnel .magesail-actions {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.magesail-tunnel .magesail-copied {
|
||||
background-color: #006400 !important;
|
||||
border-color: #006400 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.magesail-tunnel .magesail-copied span {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user