Check Update
This commit is contained in:
@@ -638,3 +638,14 @@ select:disabled {
|
||||
color:#f3f7ff;
|
||||
font-size:.78rem;
|
||||
}
|
||||
|
||||
/* Git update status card */
|
||||
.update-commit{
|
||||
font-family:ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",monospace;
|
||||
font-size:1rem!important;
|
||||
letter-spacing:.01em!important;
|
||||
overflow-wrap:anywhere;
|
||||
}
|
||||
.update-check-time{font-size:.9rem!important;letter-spacing:0!important;}
|
||||
.update-statusbar{align-items:center;gap:12px;flex-wrap:wrap;}
|
||||
.btn-xs{padding:5px 8px!important;font-size:.67rem!important;border-radius:9px!important;}
|
||||
|
||||
@@ -96,6 +96,22 @@ Object.assign(I18N_ALIASES, {
|
||||
"Public Key — share with dnstt clients":"Public Key — share with dnstt clients","Chave pública — compartilhe com clientes DNSTT":"Public Key — share with dnstt clients",
|
||||
"Máx. sessões UDP por cliente":"Max UDP Sessions Per Client","(não é o total de usuários do servidor)":"(not total server users)","Nome do serviço":"Service Name","Modo":"Mode","Protocolo":"Protocol","Porta":"Port","IP de listen":"Listen IP","Método":"Method","Caminho":"Path","Destino":"Dest","ID curto":"Short ID","Nome do servidor":"Server Name","Caminho do arquivo cert":"Cert File Path","Caminho do arquivo key":"Key File Path","Fonte do certificado:":"Certificate source:","Autoassinado":"Self-Signed","Colar PEM":"Paste PEM","Caminho do arquivo":"File Path","Salvar PEM":"Save PEM","Intervalo de reinício automático":"Auto Restart Interval","Atraso para reiniciar":"Restart Grace Delay","0s/off desativa":"0s/off disables","Chave pública":"Public Key","Nome do domínio":"Domain Name"
|
||||
});
|
||||
Object.assign(I18N_TEXT["en-US"], {
|
||||
"Panel Updates":"Panel Updates","Checking…":"Checking…","Open Git":"Open Git","Check now":"Check now","Installed version":"Installed version","Latest Git version":"Latest Git version","Branch":"Branch","Last checked":"Last checked",
|
||||
"Comparing the installed version with the Git repository.":"Comparing the installed version with the Git repository.","To update:":"To update:","Copy command":"Copy command","Up to date":"Up to date","Update available":"Update available","Local changes":"Local changes","Unknown":"Unknown",
|
||||
"The installed version matches the latest commit on {branch}.":"The installed version matches the latest commit on {branch}.","A newer commit is available on {branch}.":"A newer commit is available on {branch}.","This build contains local changes, so it cannot be compared safely.":"This build contains local changes, so it cannot be compared safely.",
|
||||
"Could not check for updates: {error}":"Could not check for updates: {error}","The update status could not be determined.":"The update status could not be determined.","Checking repository…":"Checking repository…","Update check timed out.":"Update check timed out.","Could not reach the Git repository.":"Could not reach the Git repository.","Current build commit is unavailable.":"Current build commit is unavailable.","Unknown update-check error.":"Unknown update-check error.","Copied":"Copied"
|
||||
});
|
||||
Object.assign(I18N_TEXT["pt-BR"], {
|
||||
"Panel Updates":"Atualizações do painel","Checking…":"Verificando…","Open Git":"Abrir Git","Check now":"Verificar agora","Installed version":"Versão instalada","Latest Git version":"Última versão no Git","Branch":"Branch","Last checked":"Última verificação",
|
||||
"Comparing the installed version with the Git repository.":"Comparando a versão instalada com o repositório Git.","To update:":"Para atualizar:","Copy command":"Copiar comando","Up to date":"Atualizado","Update available":"Atualização disponível","Local changes":"Alterações locais","Unknown":"Desconhecido",
|
||||
"The installed version matches the latest commit on {branch}.":"A versão instalada corresponde ao commit mais recente da branch {branch}.","A newer commit is available on {branch}.":"Existe um commit mais recente disponível na branch {branch}.","This build contains local changes, so it cannot be compared safely.":"Esta compilação contém alterações locais e não pode ser comparada com segurança.",
|
||||
"Could not check for updates: {error}":"Não foi possível verificar atualizações: {error}","The update status could not be determined.":"Não foi possível determinar o status da atualização.","Checking repository…":"Verificando o repositório…","Update check timed out.":"A verificação de atualização excedeu o tempo limite.","Could not reach the Git repository.":"Não foi possível acessar o repositório Git.","Current build commit is unavailable.":"O commit da compilação atual não está disponível.","Unknown update-check error.":"Erro desconhecido ao verificar atualização.","Copied":"Copiado"
|
||||
});
|
||||
Object.assign(I18N_ALIASES, {
|
||||
"Atualizações do painel":"Panel Updates","Verificando…":"Checking…","Abrir Git":"Open Git","Verificar agora":"Check now","Versão instalada":"Installed version","Última versão no Git":"Latest Git version","Última verificação":"Last checked",
|
||||
"Comparando a versão instalada com o repositório Git.":"Comparing the installed version with the Git repository.","Para atualizar:":"To update:","Copiar comando":"Copy command","Atualizado":"Up to date","Atualização disponível":"Update available","Alterações locais":"Local changes","Desconhecido":"Unknown"
|
||||
});
|
||||
const I18N_REVERSE = Object.fromEntries(SUPPORTED_LANGS.map(lang => [lang, Object.fromEntries(Object.entries(I18N_TEXT[lang] || {}).map(([k, v]) => [v, k]))]));
|
||||
let currentLang = detectInitialLanguage();
|
||||
let i18nTranslating = false;
|
||||
|
||||
@@ -124,6 +124,7 @@ function initAfterLogin() {
|
||||
|
||||
if (currentRole === "superadmin") {
|
||||
loadDashboardStats();
|
||||
if (typeof loadUpdateStatus === "function") loadUpdateStatus();
|
||||
statsTimer = setInterval(() => {
|
||||
loadDashboardStats();
|
||||
if (currentTab === "stats") loadStats();
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
// ─── Git update status ───────────────────────────────────────────────────────
|
||||
const DRAGON_UPDATE_COMMAND = "sudo bash /opt/sshpanel/update.sh";
|
||||
|
||||
function updateStatusErrorText(error) {
|
||||
switch (String(error || "")) {
|
||||
case "remote update check timed out":
|
||||
return t("Update check timed out.");
|
||||
case "could not read the remote Git branch":
|
||||
return t("Could not reach the Git repository.");
|
||||
case "current build commit is unavailable":
|
||||
return t("Current build commit is unavailable.");
|
||||
default:
|
||||
return error || t("Unknown update-check error.");
|
||||
}
|
||||
}
|
||||
|
||||
function setUpdateState(label, tone = "") {
|
||||
const chip = document.getElementById("updateStateChip");
|
||||
if (!chip) return;
|
||||
chip.className = "chip" + (tone ? ` ${tone}` : "");
|
||||
chip.textContent = label;
|
||||
}
|
||||
|
||||
function setCommitValue(elementID, shortValue, fullValue) {
|
||||
const el = document.getElementById(elementID);
|
||||
if (!el) return;
|
||||
el.textContent = shortValue || "--";
|
||||
el.title = fullValue || "";
|
||||
}
|
||||
|
||||
function renderUpdateStatus(data) {
|
||||
setCommitValue("updateCurrentCommit", data.current_commit_short, data.current_commit);
|
||||
setCommitValue("updateLatestCommit", data.latest_commit_short, data.latest_commit);
|
||||
|
||||
const branch = document.getElementById("updateBranch");
|
||||
if (branch) branch.textContent = data.branch || "main";
|
||||
|
||||
const checked = document.getElementById("updateCheckedAt");
|
||||
if (checked) {
|
||||
const date = data.checked_at ? new Date(data.checked_at) : null;
|
||||
checked.textContent = date && Number.isFinite(date.getTime()) ? date.toLocaleString() : "--";
|
||||
}
|
||||
|
||||
const repoLink = document.getElementById("updateRepoLink");
|
||||
if (repoLink && data.repo_web_url) repoLink.href = data.repo_web_url;
|
||||
|
||||
const text = document.getElementById("updateStatusText");
|
||||
const commandWrap = document.getElementById("updateCommandWrap");
|
||||
commandWrap?.classList.toggle("hidden", !data.update_available);
|
||||
|
||||
if (data.status === "up_to_date") {
|
||||
setUpdateState(t("Up to date"), "green");
|
||||
if (text) text.textContent = t("The installed version matches the latest commit on {branch}.", { branch: data.branch || "main" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.status === "update_available") {
|
||||
setUpdateState(t("Update available"), "warn");
|
||||
if (text) text.textContent = t("A newer commit is available on {branch}.", { branch: data.branch || "main" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.status === "local_changes") {
|
||||
setUpdateState(t("Local changes"), "warn");
|
||||
if (text) text.textContent = t("This build contains local changes, so it cannot be compared safely.");
|
||||
return;
|
||||
}
|
||||
|
||||
setUpdateState(t("Unknown"), "red");
|
||||
if (text) {
|
||||
text.textContent = data.error
|
||||
? t("Could not check for updates: {error}", { error: updateStatusErrorText(data.error) })
|
||||
: t("The update status could not be determined.");
|
||||
}
|
||||
}
|
||||
|
||||
async function loadUpdateStatus(force = false) {
|
||||
if (currentRole !== "superadmin") return;
|
||||
|
||||
const button = document.getElementById("checkUpdateBtn");
|
||||
const text = document.getElementById("updateStatusText");
|
||||
if (button) button.disabled = true;
|
||||
setUpdateState(t("Checking…"));
|
||||
if (text) text.textContent = t("Checking repository…");
|
||||
|
||||
try {
|
||||
const path = "/api/system/update-status" + (force ? "?refresh=1" : "");
|
||||
const res = await api(path);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
renderUpdateStatus(await res.json());
|
||||
} catch (error) {
|
||||
setUpdateState(t("Unknown"), "red");
|
||||
if (text) text.textContent = t("Could not check for updates: {error}", { error: error.message || t("Network error.") });
|
||||
} finally {
|
||||
if (button) button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("checkUpdateBtn")?.addEventListener("click", () => loadUpdateStatus(true));
|
||||
document.getElementById("copyUpdateCommandBtn")?.addEventListener("click", async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(DRAGON_UPDATE_COMMAND);
|
||||
} catch {
|
||||
const input = document.createElement("textarea");
|
||||
input.value = DRAGON_UPDATE_COMMAND;
|
||||
input.style.position = "fixed";
|
||||
input.style.opacity = "0";
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
input.remove();
|
||||
}
|
||||
const button = document.getElementById("copyUpdateCommandBtn");
|
||||
if (button) {
|
||||
const oldText = button.textContent;
|
||||
button.textContent = t("Copied");
|
||||
setTimeout(() => { button.textContent = oldText; }, 1400);
|
||||
}
|
||||
});
|
||||
+32
-11
@@ -16,7 +16,7 @@
|
||||
setTimeout(function(){document.documentElement.classList.remove("i18n-pending");},2500);
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="assets/app.css?v=20260511visualsafesave1"/>
|
||||
<link rel="stylesheet" href="assets/app.css?v=20260711updatecheck1"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
@@ -158,6 +158,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card superadmin-only hidden" id="updateStatusCard" style="margin-top:18px;">
|
||||
<div class="card-hdr">
|
||||
<div class="card-title">Panel Updates <span class="chip" id="updateStateChip">Checking…</span></div>
|
||||
<div class="card-actions">
|
||||
<a class="btn btn-ghost btn-sm" id="updateRepoLink" href="https://git.dr2.site/penguinehis/DragonCoreSSH-NewWEB" target="_blank" rel="noopener noreferrer">Open Git</a>
|
||||
<button class="btn btn-ghost btn-sm" type="button" id="checkUpdateBtn">Check now</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metrics">
|
||||
<div class="metric"><div class="m-label">Installed version</div><div class="m-val update-commit" id="updateCurrentCommit">--</div></div>
|
||||
<div class="metric"><div class="m-label">Latest Git version</div><div class="m-val update-commit" id="updateLatestCommit">--</div></div>
|
||||
<div class="metric"><div class="m-label">Branch</div><div class="m-val update-commit" id="updateBranch">main</div></div>
|
||||
<div class="metric"><div class="m-label">Last checked</div><div class="m-val update-check-time" id="updateCheckedAt">--</div></div>
|
||||
</div>
|
||||
<div class="statusbar update-statusbar">
|
||||
<span id="updateStatusText">Comparing the installed version with the Git repository.</span>
|
||||
<span class="hint hidden" id="updateCommandWrap">To update: <code>sudo bash /opt/sshpanel/update.sh</code> <button class="btn btn-ghost btn-xs" type="button" id="copyUpdateCommandBtn">Copy command</button></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid2 dashboard-lower">
|
||||
<div class="card hidden" id="dnsttDashboardCard">
|
||||
<div class="card-hdr">
|
||||
@@ -1241,15 +1261,16 @@
|
||||
<!-- app.js was split into ordered modules for maintainability. They are plain
|
||||
classic scripts sharing one global scope; `defer` preserves execution order,
|
||||
so behavior is identical to the old single file. Keep this load order. -->
|
||||
<script defer src="assets/js/01-core.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/02-shell.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/03-ssh-users.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/04-xray.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/05-resellers.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/06-servers.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/07-stats-logs.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/08-server-config.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/09-xray-wizard.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/10-boot.js?v=20260704ytquicfix1"></script>
|
||||
<script defer src="assets/js/01-core.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/02-shell.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/03-ssh-users.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/04-xray.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/05-resellers.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/06-servers.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/07-stats-logs.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/08-server-config.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/09-xray-wizard.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/11-update-status.js?v=20260711updatecheck1"></script>
|
||||
<script defer src="assets/js/10-boot.js?v=20260711updatecheck1"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user