Skip to content

Commit 1366b77

Browse files
Merge pull request #542 from ExtremeFiretop/Minimum-Firmware-Check
Minimum Firmware Check Before Script Updates
2 parents 9964744 + 52c1b03 commit 1366b77

File tree

4 files changed

+289
-25
lines changed

4 files changed

+289
-25
lines changed

MerlinAU.asp

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ var isEMailConfigEnabledInAMTM = false;
6868
var scriptAutoUpdateCronSchedHR = 'TBD';
6969
var fwAutoUpdateCheckCronSchedHR = 'TBD';
7070
var isScriptUpdateAvailable = 'TBD';
71+
var fwUpdateEstimatedRunDate = 'TBD';
72+
var MinimumScriptFWRequired = 'TBD';
73+
74+
let pendingScriptUpdateGateCheck = false;
75+
let scriptUpdateGateTries = 0;
76+
const scriptUpdateGateMaxTries = 12;
7177
7278
const validationErrorMsg = 'Validation failed. Please correct invalid value and try again.';
7379
@@ -170,7 +176,7 @@ function FormatNumericSetting (formInput)
170176
const numberRegExp = '^[0-9]+$';
171177
const daysOfWeekNumbr = ['0', '1', '2', '3', '4', '5', '6'];
172178
const daysOfWeekNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
173-
const daysOfWeekRexpN = '([S|s]un|[M|m]on|[T|t]ue|[W|w]ed|[T|t]hu|[F|f]ri|[S|s]at)';
179+
const daysOfWeekRexpN = '([Ss]un|[Mm]on|[Tt]ue|[Ww]ed|[Tt]hu|[Ff]ri|[Ss]at)';
174180
const daysOfWeekRexp1 = `${daysOfWeekRexpN}|[0-6]`;
175181
const daysOfWeekRexp2 = `${daysOfWeekRexpN}[-]${daysOfWeekRexpN}|[0-6][-][0-6]`;
176182
const daysOfWeekRexp3 = `${daysOfWeekRexpN}([,]${daysOfWeekRexpN})+|[0-6]([,][0-6])+`;
@@ -248,6 +254,84 @@ const fwScheduleTime =
248254
}
249255
};
250256
257+
function ExtractFWVersion(verStr)
258+
{
259+
if (!verStr) { return ''; }
260+
let match = String(verStr).trim().match(/\d+(?:\.\d+)+/);
261+
return match ? match[0] : String(verStr).trim();
262+
}
263+
264+
// Prefer hidden #firmver (same nvram source, no formatting risk), else fallback to #fwVersionInstalled
265+
function GetInstalledFWVersionFromUI()
266+
{
267+
let firmverInput = document.getElementById('firmver');
268+
if (firmverInput && firmverInput.value)
269+
{ return ExtractFWVersion(firmverInput.value); }
270+
271+
let fwCell = document.getElementById('fwVersionInstalled');
272+
if (fwCell)
273+
{ return ExtractFWVersion(fwCell.textContent); }
274+
275+
return '';
276+
}
277+
278+
function RunScriptUpdateFirmwareGateCheck()
279+
{
280+
if (!pendingScriptUpdateGateCheck) { return; }
281+
282+
scriptUpdateGateTries++;
283+
284+
$.ajax({
285+
url: '/ext/MerlinAU/checkHelper.js?_=' + new Date().getTime(),
286+
dataType: 'script',
287+
timeout: 5000,
288+
289+
success: function()
290+
{
291+
let requiredStr = ExtractFWVersion(MinimumScriptFWRequired);
292+
let installedStr = GetInstalledFWVersionFromUI();
293+
294+
// If required version isn't ready yet, retry a few times
295+
if (!requiredStr || requiredStr === 'TBD')
296+
{
297+
if (scriptUpdateGateTries < scriptUpdateGateMaxTries)
298+
{ setTimeout(RunScriptUpdateFirmwareGateCheck, 1000); }
299+
else
300+
{ pendingScriptUpdateGateCheck = false; isFormSubmitting = false; }
301+
return;
302+
}
303+
304+
let installedNum = FWVersionStrToNum(installedStr);
305+
let requiredNum = FWVersionStrToNum(requiredStr);
306+
307+
pendingScriptUpdateGateCheck = false;
308+
isFormSubmitting = false;
309+
310+
if (installedNum === 0 || requiredNum === 0) { return; }
311+
312+
if (installedNum < requiredNum)
313+
{
314+
alert(
315+
"**SCRIPT UPDATE BLOCKED**\n\n" +
316+
"MerlinAU cannot update because your installed firmware is " +
317+
"below the minimum required firmware for this script update.\n\n" +
318+
"Installed firmware: " + installedStr + "\n" +
319+
"Minimum required: " + requiredStr + "\n\n" +
320+
"Please update your router firmware, then try again."
321+
);
322+
}
323+
},
324+
325+
error: function()
326+
{
327+
if (scriptUpdateGateTries < scriptUpdateGateMaxTries)
328+
{ setTimeout(RunScriptUpdateFirmwareGateCheck, 1000); }
329+
else
330+
{ pendingScriptUpdateGateCheck = false; isFormSubmitting = false; }
331+
}
332+
});
333+
}
334+
251335
/**-------------------------------------**/
252336
/** Added by Martinski W. [2025-Jan-24] **/
253337
/**-------------------------------------**/
@@ -910,6 +994,10 @@ function ShowLatestChangelog(e)
910994
box.scrollTop -= 40;
911995
ev.preventDefault();
912996
break;
997+
case 'Escape':
998+
$('#changelogModal').hide();
999+
ev.preventDefault();
1000+
break;
9131001
default:
9141002
break;
9151003
}
@@ -997,7 +1085,7 @@ function ValidateDirectoryPath (formField, dirType)
9971085
function GetExternalCheckResults()
9981086
{
9991087
$.ajax({
1000-
url: '/ext/MerlinAU/checkHelper.js',
1088+
url: '/ext/MerlinAU/checkHelper.js?_=' + new Date().getTime(),
10011089
dataType: 'script',
10021090
timeout: 5000,
10031091
error: function(xhr){
@@ -1537,7 +1625,10 @@ function GetLoginPswdCheckStatus()
15371625
15381626
document.getElementById('LoginPswdStatusText').textContent = pswdStatusText;
15391627
showhide('LoginPswdStatusText',true);
1540-
loginPswdHint = loginPswdHint.replace (/PswdSTATUS/, pswdStatusHint1);
1628+
// Rebuild the base hint fresh EACH time (so PswdSTATUS always exists) //
1629+
let loginUserStr = document.getElementById('http_username')?.value.trim() || 'admin';
1630+
let baseHint = loginPswdStatHintMsg.replace(/LoginUSER/, loginUserStr);
1631+
loginPswdHint = baseHint.replace(/PswdSTATUS/, pswdStatusHint1);
15411632
15421633
pswdField = document.getElementById('routerPassword');
15431634
if (passwordFailed || pswdVerified || pswdUnverified)
@@ -1688,7 +1779,7 @@ function InitializeFields()
16881779
if (script_AutoUpdate_Check)
16891780
{
16901781
script_AutoUpdate_Check.checked = (custom_settings.Allow_Script_Auto_Update === 'ENABLED');
1691-
UpdateForceScriptCheckboxState(script_AutoUpdate_Check?.checked);
1782+
UpdateForceScriptCheckboxState(script_AutoUpdate_Check && script_AutoUpdate_Check.checked);
16921783
}
16931784
16941785
if (betaToReleaseUpdatesEnabled)
@@ -2132,7 +2223,14 @@ function initial()
21322223
hiddenFrame.onload = function()
21332224
{
21342225
console.log("Hidden frame loaded with server response.");
2226+
2227+
if (pendingScriptUpdateGateCheck)
2228+
{
2229+
// Wait a moment to allow the backend logic to finish writing checkHelper.js
2230+
setTimeout(RunScriptUpdateFirmwareGateCheck, 1000);
2231+
}
21352232
};
2233+
21362234
initializeCollapsibleSections();
21372235
}
21382236
}
@@ -2142,6 +2240,9 @@ function initial()
21422240
/**----------------------------------------**/
21432241
function SaveCombinedConfig()
21442242
{
2243+
// Reset containers per-save so stale values never leak forward //
2244+
advanced_settings = {};
2245+
21452246
// Clear the hidden field before saving //
21462247
document.getElementById('amng_custom').value = '';
21472248
@@ -2386,6 +2487,10 @@ function UpdateMerlinAUScript()
23862487
? 'start_MerlinAUscrptupdate_force'
23872488
: 'start_MerlinAUscrptupdate';
23882489
2490+
pendingScriptUpdateGateCheck = true;
2491+
scriptUpdateGateTries = 0;
2492+
isFormSubmitting = true;
2493+
23892494
document.form.action_script.value = actionScriptValue;
23902495
document.form.action_wait.value = 10;
23912496
showLoading();
@@ -2612,7 +2717,6 @@ function initializeCollapsibleSections()
26122717
<input type="hidden" id="nvram_model" value="<% nvram_get("model"); %>" />
26132718
<input type="hidden" id="nvram_build_name" value="<% nvram_get("build_name"); %>" />
26142719
<input type="hidden" id="nvram_productid" value="<% nvram_get("productid"); %>" />
2615-
<input type="hidden" name="installedfirm" value="<% nvram_get("innerver"); %>" />
26162720
<input type="hidden" name="amng_custom" id="amng_custom" value="" />
26172721
26182722
<table class="content" cellpadding="0" cellspacing="0" style="margin:0 auto;">
@@ -2636,7 +2740,7 @@ function initializeCollapsibleSections()
26362740
<div class="formfonttitle" id="headerTitle" style="text-align:center;">MerlinAU</div>
26372741
<div style="margin:10px 0 10px 5px;" class="splitLine"></div>
26382742
<div class="formfontdesc">This is the MerlinAU add-on integrated into the router WebUI
2639-
<span style="margin-left:8px;" id="WikiURL"">[
2743+
<span style="margin-left:8px;" id="WikiURL">[
26402744
<a style="font-weight:bolder; text-decoration:underline; cursor:pointer;"
26412745
href="https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router/wiki"
26422746
title="Go to MerlinAU Wiki page" target="_blank">Wiki</a> ]
@@ -2761,7 +2865,7 @@ function initializeCollapsibleSections()
27612865
<input type="submit" id="FWUpdateCheckButton" onclick="CheckFirmwareUpdate();
27622866
return false;" value="F/W Update Check" class="button_gen savebutton" name="button">
27632867
<br>
2764-
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8x">
2868+
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8px">
27652869
<input type="checkbox" checked="" id="BypassPostponedDays" name="BypassPostponedDays"
27662870
style="padding:0; vertical-align:middle; position:relative; margin-left:-5px; margin-top:5px; margin-bottom:8px"/>Bypass postponed days</label>
27672871
</br>
@@ -2770,7 +2874,7 @@ function initializeCollapsibleSections()
27702874
<input type="submit" id="LatestChangelogButton" onclick="ShowLatestChangelog();
27712875
return false;" value="Latest Changelog" class="button_gen savebutton" title="View the latest changelog" name="button">
27722876
<br>
2773-
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8x">
2877+
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8px">
27742878
<input type="checkbox" id="approveChangelogCheck" name="approveChangelogCheck" onclick="ToggleChangelogApproval(this);"
27752879
style="padding:0; vertical-align:middle; position:relative; margin-left:-5px; margin-top:5px; margin-bottom:8px"/>Approve changelog</label>
27762880
</br>
@@ -2794,7 +2898,7 @@ function initializeCollapsibleSections()
27942898
<input type="submit" id="UninstallButton" onclick="Uninstall(); return false;"
27952899
value="Uninstall" class="button_gen savebutton" name="button">
27962900
<br>
2797-
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8x">
2901+
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8px">
27982902
<input type="checkbox" checked="" id="KeepConfigFile" name="KeepConfigFile"
27992903
style="padding:0; vertical-align:middle; position:relative; margin-left:-3px; margin-top:5px; margin-bottom:8px"/>Keep configuration file</label>
28002904
</br>
@@ -2812,7 +2916,7 @@ function initializeCollapsibleSections()
28122916
<tbody>
28132917
<tr>
28142918
<td colspan="2">
2815-
<form id="advancedOptionsForm">
2919+
<div id="advancedOptionsForm">
28162920
<table class="FormTable SettingsTable" width="100%" border="0" cellpadding="5" cellspacing="5" style="table-layout: fixed;">
28172921
<colgroup>
28182922
<col style="width: 37%;" />
@@ -2838,7 +2942,7 @@ function initializeCollapsibleSections()
28382942
onblur="ValidatePasswordString(this,'onBLUR')"
28392943
onkeyup="ValidatePasswordString(this,'onKEYUP')"/>
28402944
<div id="eyeToggle" onclick="togglePassword();"
2841-
style="position: absolute; display: inline-block; margin-left: 5px; vertical-align: middle; width:24px; height:24px; background:url('/images/icon-visible@2x.png') no-repeat center; background-size: contain; cursor: pointer;">
2945+
style="position: relative; display: inline-block; margin-left: 5px; vertical-align: middle; width:24px; height:24px; background:url('/images/icon-visible@2x.png') no-repeat center; background-size: contain; cursor: pointer;">
28422946
</div>
28432947
</div>
28442948
<br>
@@ -3086,7 +3190,7 @@ function initializeCollapsibleSections()
30863190
<input type="submit" onclick="SaveCombinedConfig(); return false;"
30873191
value="Save Configuration" class="button_gen savebutton" name="button">
30883192
</div>
3089-
</form></td></tr></tbody></table>
3193+
</div></td></tr></tbody></table>
30903194
<div id="footerTitle" style="margin-top:10px;text-align:center;">MerlinAU</div>
30913195
</td></tr></tbody></table></td></tr></table></td>
30923196
<td width="10"></td>

0 commit comments

Comments
 (0)