fix: disable all config watchers (#1306)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Updated CLI operations now use sequential stop and start actions for
service reloads and SSO user modifications, ensuring a more controlled
update process.
- Introduced a new `<uui-toaster>` component for notifications,
replacing the previous jGrowl notification system and enhancing user
experience.
- Added functionality for managing live updates based on window focus,
improving responsiveness during user interaction.

- **Refactor**
- Streamlined configuration updates by removing automatic monitoring,
resulting in a simpler update workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Eli Bosley
2025-04-02 13:09:47 -04:00
committed by GitHub
parent 9004313ff8
commit 5c1b4352cf
12 changed files with 264 additions and 79 deletions

View File

@@ -9,7 +9,6 @@ import { store } from '@app/store/index.js';
import { syncInfoApps } from '@app/store/sync/info-apps-sync.js';
import { syncRegistration } from '@app/store/sync/registration-sync.js';
import { FileLoadStatus } from '@app/store/types.js';
import { setupConfigPathWatch } from '@app/store/watch/config-watch.js';
export const startStoreSync = async () => {
// The last state is stored so we don't end up in a loop of writing -> reading -> writing
@@ -45,6 +44,4 @@ export const startStoreSync = async () => {
lastState = state;
});
setupConfigPathWatch();
};

View File

@@ -1,39 +0,0 @@
import { existsSync, writeFileSync } from 'fs';
import { watch } from 'chokidar';
import { logger } from '@app/core/log.js';
import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer.js';
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer.js';
import { CHOKIDAR_USEPOLLING, ENVIRONMENT } from '@app/environment.js';
import { getters, store } from '@app/store/index.js';
import { initialState, loadConfigFile, logoutUser } from '@app/store/modules/config.js';
export const setupConfigPathWatch = () => {
const myServersConfigPath = getters.paths()?.['myservers-config'];
if (myServersConfigPath) {
logger.info('Watch Setup on Config Path: %s', myServersConfigPath);
if (!existsSync(myServersConfigPath)) {
const config = safelySerializeObjectToIni(getWriteableConfig(initialState, 'flash'));
writeFileSync(myServersConfigPath, config, 'utf-8');
}
const watcher = watch(myServersConfigPath, {
persistent: true,
ignoreInitial: false,
usePolling: CHOKIDAR_USEPOLLING === true,
})
.on('change', async (change) => {
logger.trace('Config File Changed, Reloading Config %s', change);
await store.dispatch(loadConfigFile());
})
.on('unlink', async () => {
const config = safelySerializeObjectToIni(getWriteableConfig(initialState, 'flash'));
await writeFileSync(myServersConfigPath, config, 'utf-8');
watcher.close();
setupConfigPathWatch();
store.dispatch(logoutUser({ reason: 'Config File was Deleted' }));
});
} else {
logger.error('[FATAL] Failed to setup watch on My Servers Config (Could Not Read Config Path)');
}
};

View File

@@ -6,7 +6,8 @@ import { loadConfigFile, updateUserConfig } from '@app/store/modules/config.js';
import { writeConfigSync } from '@app/store/sync/config-disk-sync.js';
import { DeveloperQuestions } from '@app/unraid-api/cli/developer/developer.questions.js';
import { LogService } from '@app/unraid-api/cli/log.service.js';
import { RestartCommand } from '@app/unraid-api/cli/restart.command.js';
import { StartCommand } from '@app/unraid-api/cli/start.command.js';
import { StopCommand } from '@app/unraid-api/cli/stop.command.js';
interface DeveloperOptions {
disclaimer: boolean;
@@ -21,7 +22,8 @@ export class DeveloperCommand extends CommandRunner {
constructor(
private logger: LogService,
private readonly inquirerService: InquirerService,
private readonly restartCommand: RestartCommand
private readonly startCommand: StartCommand,
private readonly stopCommand: StopCommand
) {
super();
}
@@ -33,6 +35,7 @@ export class DeveloperCommand extends CommandRunner {
}
const { store } = await import('@app/store/index.js');
await store.dispatch(loadConfigFile());
await this.stopCommand.run([]);
store.dispatch(updateUserConfig({ local: { sandbox: options.sandbox ? 'yes' : 'no' } }));
writeConfigSync('flash');
@@ -40,6 +43,6 @@ export class DeveloperCommand extends CommandRunner {
'Updated Developer Configuration - restart the API in 5 seconds to apply them...'
);
await new Promise((resolve) => setTimeout(resolve, 5000));
await this.restartCommand.run([]);
await this.startCommand.run([], {});
}
}

View File

@@ -9,6 +9,8 @@ import { writeConfigSync } from '@app/store/sync/config-disk-sync.js';
import { LogService } from '@app/unraid-api/cli/log.service.js';
import { RestartCommand } from '@app/unraid-api/cli/restart.command.js';
import { AddSSOUserQuestionSet } from '@app/unraid-api/cli/sso/add-sso-user.questions.js';
import { StartCommand } from '@app/unraid-api/cli/start.command.js';
import { StopCommand } from '@app/unraid-api/cli/stop.command.js';
interface AddSSOUserCommandOptions {
disclaimer: string;
@@ -25,7 +27,8 @@ export class AddSSOUserCommand extends CommandRunner {
constructor(
private readonly logger: LogService,
private readonly inquirerService: InquirerService,
private readonly restartCommand: RestartCommand
private readonly startCommand: StartCommand,
private readonly stopCommand: StopCommand
) {
super();
}
@@ -34,16 +37,12 @@ export class AddSSOUserCommand extends CommandRunner {
try {
options = await this.inquirerService.prompt(AddSSOUserQuestionSet.name, options);
if (options.disclaimer === 'y' && options.username) {
await this.stopCommand.run([]);
await store.dispatch(loadConfigFile());
const shouldRestart = store.getState().config.remote.ssoSubIds.length === 0;
store.dispatch(addSsoUser(options.username));
writeConfigSync('flash');
this.logger.info(`User added ${options.username}`);
if (shouldRestart) {
this.logger.info('Restarting the Unraid API in 5 seconds to enable the SSO button');
await new Promise((resolve) => setTimeout(resolve, 5000));
await this.restartCommand.run([]);
}
this.logger.info(`User added ${options.username}, starting the API`);
await this.startCommand.run([], {});
}
} catch (e: unknown) {
if (e instanceof Error) {

View File

@@ -1,12 +1,14 @@
import { Injectable } from '@nestjs/common';
import { CommandRunner, InquirerService, Option, OptionChoiceFor, SubCommand } from 'nest-commander';
import { CommandRunner, InquirerService, Option, SubCommand } from 'nest-commander';
import { store } from '@app/store/index.js';
import { loadConfigFile, removeSsoUser } from '@app/store/modules/config.js';
import { writeConfigSync } from '@app/store/sync/config-disk-sync.js';
import { LogService } from '@app/unraid-api/cli/log.service.js';
import { RemoveSSOUserQuestionSet } from '@app/unraid-api/cli/sso/remove-sso-user.questions.js';
import { StartCommand } from '@app/unraid-api/cli/start.command.js';
import { StopCommand } from '@app/unraid-api/cli/stop.command.js';
interface RemoveSSOUserCommandOptions {
username: string;
@@ -21,13 +23,17 @@ interface RemoveSSOUserCommandOptions {
export class RemoveSSOUserCommand extends CommandRunner {
constructor(
private readonly logger: LogService,
private readonly inquirerService: InquirerService
private readonly inquirerService: InquirerService,
private readonly stopCommand: StopCommand,
private readonly startCommand: StartCommand
) {
super();
}
public async run(_input: string[], options: RemoveSSOUserCommandOptions): Promise<void> {
await store.dispatch(loadConfigFile());
options = await this.inquirerService.prompt(RemoveSSOUserQuestionSet.name, options);
await this.stopCommand.run([]);
store.dispatch(removeSsoUser(options.username === 'all' ? null : options.username));
if (options.username === 'all') {
this.logger.info('All users removed from SSO');
@@ -35,6 +41,7 @@ export class RemoveSSOUserCommand extends CommandRunner {
this.logger.info('User removed: ' + options.username);
}
writeConfigSync('flash');
await this.startCommand.run([], {});
}
@Option({

View File

@@ -105,10 +105,14 @@ if (!file_exists($notes)) file_put_contents($notes,shell_exec("$docroot/plugins/
<script>
String.prototype.actionName = function(){return this.split(/[\\/]/g).pop();}
String.prototype.channel = function(){return this.split(':')[1].split(',').findIndex((e)=>/\[\d\]/.test(e));}
NchanSubscriber.prototype.monitor = function(){subscribers.push(this);}
Shadowbox.init({skipSetup:true});
context.init();
// list of nchan subscribers to start/stop at focus change
var subscribers = [];
// server uptime
var uptime = <?=strtok(exec("cat /proc/uptime"),' ')?>;
var expiretime = <?=_var($var,'regTy')=='Trial'||strstr(_var($var,'regTy'),'expired')?_var($var,'regTm2'):0?>;
@@ -140,6 +144,7 @@ function pauseEvents(id) {
if (!id || i==id) clearTimeout(timer);
});
}
function resumeEvents(id,delay) {
var startDelay = delay||50;
$.each(timers, function(i,timer) {
@@ -147,9 +152,11 @@ function resumeEvents(id,delay) {
startDelay += 50;
});
}
function plus(value,single,plural,last) {
return value>0 ? (value+' '+(value==1?single:plural)+(last?'':', ')) : '';
}
function updateTime() {
var now = new Date();
var days = parseInt(uptime/86400);
@@ -179,6 +186,7 @@ function updateTime() {
}
setTimeout(updateTime,1000);
}
function refresh(top) {
if (typeof top === 'undefined') {
for (var i=0,element; element=document.querySelectorAll('input,button,select')[i]; i++) {element.disabled = true;}
@@ -189,11 +197,13 @@ function refresh(top) {
location.reload();
}
}
function initab(page) {
$.removeCookie('one');
$.removeCookie('tab');
if (page != null) location.replace(page);
}
function settab(tab) {
<?switch ($myPage['name']):?>
<?case'Main':?>
@@ -209,6 +219,7 @@ function settab(tab) {
$.cookie('one',tab);
<?endswitch;?>
}
function done(key) {
var url = location.pathname.split('/');
var path = '/'+url[1];
@@ -216,10 +227,12 @@ function done(key) {
$.removeCookie('one');
location.replace(path);
}
function chkDelete(form, button) {
button.value = form.confirmDelete.checked ? "<?=_('Delete')?>" : "<?=_('Apply')?>";
button.disabled = false;
}
function makeWindow(name,height,width) {
var top = (screen.height-height)/2;
if (top < 0) {top = 0; height = screen.availHeight;}
@@ -227,6 +240,7 @@ function makeWindow(name,height,width) {
if (left < 0) {left = 0; width = screen.availWidth;}
return window.open('',name,'resizeable=yes,scrollbars=yes,height='+height+',width='+width+',top='+top+',left='+left);
}
function openBox(cmd,title,height,width,load,func,id) {
// open shadowbox window (run in foreground)
// included for legacy purposes, replaced by openPlugin
@@ -235,6 +249,7 @@ function openBox(cmd,title,height,width,load,func,id) {
var options = load ? (func ? {modal:true,onClose:function(){setTimeout(func+'('+'"'+(id||'')+'")');}} : {modal:true,onClose:function(){location.reload();}}) : {modal:false};
Shadowbox.open({content:run, player:'iframe', title:title, height:Math.min(screen.availHeight,800), width:Math.min(screen.availWidth,1200), options:options});
}
function openWindow(cmd,title,height,width) {
// open regular window (run in background)
// included for legacy purposes, replaced by openTerminal
@@ -252,6 +267,7 @@ function openWindow(cmd,title,height,width) {
makeWindow(window_name,height,width);
form.submit();
}
function openTerminal(tag,name,more) {
if (/MSIE|Edge/.test(navigator.userAgent)) {
swal({title:"_(Unsupported Feature)_",text:"_(Sorry, this feature is not supported by MSIE/Edge)_.<br>_(Please try a different browser)_",type:'error',html:true,animation:'none',confirmButtonText:"_(Ok)_"});
@@ -263,6 +279,7 @@ function openTerminal(tag,name,more) {
var socket = ['ttyd','syslog'].includes(tag) ? '/webterminal/'+tag+'/' : '/logterminal/'+name+(more=='.log'?more:'')+'/';
$.get('/webGui/include/OpenTerminal.php',{tag:tag,name:name,more:more},function(){setTimeout(function(){tty_window.location=socket; tty_window.focus();},200);});
}
function bannerAlert(text,cmd,plg,func,start) {
$.post('/webGui/include/StartCommand.php',{cmd:cmd,pid:1},function(pid) {
if (pid == 0) {
@@ -299,6 +316,7 @@ function bannerAlert(text,cmd,plg,func,start) {
}
});
}
function openPlugin(cmd,title,plg,func,start=0,button=0) {
// start = 0 : run command only when not already running (default)
// start = 1 : run command unconditionally
@@ -322,6 +340,7 @@ function openPlugin(cmd,title,plg,func,start=0,button=0) {
$('button.confirm').prop('disabled',button!=0);
});
}
function openDocker(cmd,title,plg,func,start=0,button=0) {
// start = 0 : run command only when not already running (default)
// start = 1 : run command unconditionally
@@ -345,6 +364,7 @@ function openDocker(cmd,title,plg,func,start=0,button=0) {
$('button.confirm').prop('disabled',button==0);
});
}
function openVMAction(cmd,title,plg,func,start=0,button=0) {
// start = 0 : run command only when not already running (default)
// start = 1 : run command unconditionally
@@ -368,6 +388,7 @@ function openVMAction(cmd,title,plg,func,start=0,button=0) {
$('button.confirm').prop('disabled',button==0);
});
}
function abortOperation(pid) {
swal({title:"<?=_('Abort background operation')?>",text:"<?=_('This may leave an unknown state')?>",html:true,animation:'none',type:'warning',showCancelButton:true,confirmButtonText:"<?=_('Proceed')?>",cancelButtonText:"<?=_('Cancel')?>"},function(){
$.post('/webGui/include/StartCommand.php',{kill:pid},function() {
@@ -381,6 +402,7 @@ function abortOperation(pid) {
});
});
}
function openChanges(cmd,title,nchan,button=0) {
$('div.spinner.fixed').show();
// button = 0 : hide CLOSE button (default)
@@ -397,6 +419,7 @@ function openChanges(cmd,title,nchan,button=0) {
$('button.confirm').text("<?=_('Done')?>").prop('disabled',false).show();
});
}
function openAlert(cmd,title,func) {
$.post('/webGui/include/StartCommand.php',{cmd:cmd,start:2},function(data) {
$('div.spinner.fixed').hide();
@@ -407,6 +430,7 @@ function openAlert(cmd,title,func) {
$('pre#swalbody').html(data);
});
}
function openDone(data) {
if (data == '_DONE_') {
$('div.spinner.fixed').hide();
@@ -421,6 +445,7 @@ function openDone(data) {
}
return false;
}
function openError(data) {
if (data == '_ERROR_') {
$('div.spinner.fixed').hide();
@@ -429,16 +454,20 @@ function openError(data) {
}
return false;
}
function showStatus(name,plugin,job) {
$.post('/webGui/include/ProcessStatus.php',{name:name,plugin:plugin,job:job},function(status){$(".tabs").append(status);});
}
function showFooter(data, id) {
if (id !== undefined) $('#'+id).remove();
$('#copyright').prepend(data);
}
function showNotice(data) {
$('#user-notice').html(data.replace(/<a>(.*)<\/a>/,"<a href='/Plugins'>$1</a>"));
}
function escapeQuotes(form) {
$(form).find('input[type=text]').each(function(){$(this).val($(this).val().replace(/"/g,'\\"'));});
}
@@ -516,12 +545,14 @@ function removeRebootNotice(message="<?=_('You must reboot for changes to take e
function showUpgradeChanges() { /** @note can likely be removed, not used in webgui or api repos */
openChanges("showchanges /tmp/plugins/unRAIDServer.txt","<?=_('Release Notes')?>");
}
function showUpgrade(text,noDismiss=false) { /** @note can likely be removed, not used in webgui or api repos */
if ($.cookie('os_upgrade')==null) {
if (osUpgradeWarning) removeBannerWarning(osUpgradeWarning);
osUpgradeWarning = addBannerWarning(text.replace(/<a>(.+?)<\/a>/,"<a href='#' onclick='openUpgrade()'>$1</a>").replace(/<b>(.*)<\/b>/,"<a href='#' onclick='document.rebootNow.submit()'>$1</a>"),false,noDismiss);
}
}
function hideUpgrade(set) { /** @note can likely be removed, not used in webgui or api repos */
removeBannerWarning(osUpgradeWarning);
if (set)
@@ -529,6 +560,7 @@ function hideUpgrade(set) { /** @note can likely be removed, not used in webgui
else
$.removeCookie('os_upgrade');
}
function confirmUpgrade(confirm) {
if (confirm) {
swal({title:"<?=_('Update')?> Unraid OS",text:"<?=_('Do you want to update to the new version')?>?",type:'warning',html:true,animation:'none',showCancelButton:true,closeOnConfirm:false,confirmButtonText:"<?=_('Proceed')?>",cancelButtonText:"<?=_('Cancel')?>"},function(){
@@ -538,6 +570,7 @@ function confirmUpgrade(confirm) {
openPlugin("plugin update unRAIDServer.plg","<?=_('Update')?> Unraid OS");
}
}
function openUpgrade() {
hideUpgrade();
$.get('/plugins/dynamix.plugin.manager/include/ShowPlugins.php',{cmd:'alert'},function(data) {
@@ -550,11 +583,13 @@ function openUpgrade() {
}
});
}
function digits(number) {
if (number < 10) return 'one';
if (number < 100) return 'two';
return 'three';
}
function openNotifier() {
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
$.each($.parseJSON(msg), function(i, notify){
@@ -571,6 +606,7 @@ function openNotifier() {
});
});
}
function closeNotifier() {
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
$.each($.parseJSON(msg), function(i, notify){
@@ -579,14 +615,17 @@ function closeNotifier() {
$('div.jGrowl').find('div.jGrowl-close').trigger('click');
});
}
function viewHistory() {
location.replace('/Tools/NotificationsArchive');
}
function flashReport() {
$.post('/webGui/include/Report.php',{cmd:'config'},function(check){
if (check>0) addBannerWarning("<?=_('Your flash drive is corrupted or offline').'. '._('Post your diagnostics in the forum for help').'.'?> <a target='_blank' href='https://docs.unraid.net/go/changing-the-flash-device/'><?=_('See also here')?></a>");
});
}
$(function() {
let tab;
<?switch ($myPage['name']):?>
@@ -719,7 +758,7 @@ unset($buttons,$button);
// Build page content
// Reload page every X minutes during extended viewing?
if (isset($myPage['Load']) && $myPage['Load']>0) echo "\n<script>timers.reload = setTimeout(function(){location.reload();},".($myPage['Load']*60000).");</script>\n";
if (isset($myPage['Load']) && $myPage['Load'] > 0) echo "\n<script>timers.reload = setInterval(function(){if (nchanPaused === false)location.reload();},".($myPage['Load']*60000).");</script>\n";
echo "<div class='tabs'>";
$tab = 1;
$pages = [];
@@ -864,7 +903,7 @@ function parseINI(msg) {
// unraid animated logo
var unraid_logo = '<?readfile("$docroot/webGui/images/animated-logo.svg")?>';
var defaultPage = new NchanSubscriber('/sub/session,var<?=$entity?",notify":""?>',{subscriber:'websocket'});
var defaultPage = new NchanSubscriber('/sub/session,var<?=$entity?",notify":""?>',{subscriber:'websocket', reconnectTimeout:5000});
defaultPage.on('message', function(msg,meta) {
switch (meta.id.channel()) {
case 0:
@@ -933,7 +972,7 @@ function wlanSettings() {
window.location = '/Settings/NetworkSettings';
}
var nchan_wlan0 = new NchanSubscriber('/sub/wlan0',{subscriber:'websocket'});
var nchan_wlan0 = new NchanSubscriber('/sub/wlan0',{subscriber:'websocket', reconnectTimeout:5000});
nchan_wlan0.on('message', function(msg) {
var wlan = JSON.parse(msg);
$('#wlan0').removeClass().addClass(wlan.color).attr('title',wlan.title);
@@ -941,7 +980,7 @@ nchan_wlan0.on('message', function(msg) {
nchan_wlan0.start();
<?endif;?>
var nchan_plugins = new NchanSubscriber('/sub/plugins',{subscriber:'websocket'});
var nchan_plugins = new NchanSubscriber('/sub/plugins',{subscriber:'websocket', reconnectTimeout:5000});
nchan_plugins.on('message', function(data) {
if (!data || openDone(data)) return;
var box = $('pre#swaltext');
@@ -954,7 +993,7 @@ nchan_plugins.on('message', function(data) {
box.html(text.join('<br>')).scrollTop(box[0].scrollHeight);
});
var nchan_docker = new NchanSubscriber('/sub/docker',{subscriber:'websocket'});
var nchan_docker = new NchanSubscriber('/sub/docker',{subscriber:'websocket', reconnectTimeout:5000});
nchan_docker.on('message', function(data) {
if (!data || openDone(data)) return;
var box = $('pre#swaltext');
@@ -1003,7 +1042,7 @@ nchan_docker.on('message', function(data) {
box.scrollTop(box[0].scrollHeight);
});
var nchan_vmaction = new NchanSubscriber('/sub/vmaction',{subscriber:'websocket'});
var nchan_vmaction = new NchanSubscriber('/sub/vmaction',{subscriber:'websocket', reconnectTimeout:5000});
nchan_vmaction.on('message', function(data) {
if (!data || openDone(data) || openError(data)) return;
var box = $('pre#swaltext');
@@ -1232,6 +1271,76 @@ $('body').on('click','a,.ca_href', function(e) {
}
}
});
// Start & stop live updates when window loses focus
var nchanPaused = false;
var blurTimer = false;
$(window).focus(function() {
nchanFocusStart();
});
// Stop nchan on loss of focus
<? if ( $display['liveUpdate'] == "no" ):?>
$(window).blur(function() {
blurTimer = setTimeout(function(){
nchanFocusStop();
},30000);
});
<?endif;?>
document.addEventListener("visibilitychange", (event) => {
<? if ( $display['liveUpdate'] == "no" ):?>
if (document.hidden) {
nchanFocusStop();
}
<?else:?>
if (document.hidden) {
nchanFocusStop();
} else {
nchanFocusStart();
}
<?endif;?>
});
function nchanFocusStart() {
if ( blurTimer !== false ) {
clearTimeout(blurTimer);
blurTimer = false;
}
if (nchanPaused !== false ) {
removeBannerWarning(nchanPaused);
nchanPaused = false;
try {
pageFocusFunction();
} catch(error) {}
subscribers.forEach(function(e) {
e.start();
});
}
}
function nchanFocusStop(banner=true) {
if ( subscribers.length ) {
if ( nchanPaused === false ) {
var newsub = subscribers;
subscribers.forEach(function(e) {
try {
e.stop();
} catch(err) {
newsub.splice(newsub.indexOf(e,1));
}
});
subscribers = newsub;
if ( banner && subscribers.length ) {
nchanPaused = addBannerWarning("<?=_('Live Updates Paused');?>",false,true );
}
}
}
}
</script>
</body>
</html>

View File

@@ -105,10 +105,14 @@ if (!file_exists($notes)) file_put_contents($notes,shell_exec("$docroot/plugins/
<script>
String.prototype.actionName = function(){return this.split(/[\\/]/g).pop();}
String.prototype.channel = function(){return this.split(':')[1].split(',').findIndex((e)=>/\[\d\]/.test(e));}
NchanSubscriber.prototype.monitor = function(){subscribers.push(this);}
Shadowbox.init({skipSetup:true});
context.init();
// list of nchan subscribers to start/stop at focus change
var subscribers = [];
// server uptime
var uptime = <?=strtok(exec("cat /proc/uptime"),' ')?>;
var expiretime = <?=_var($var,'regTy')=='Trial'||strstr(_var($var,'regTy'),'expired')?_var($var,'regTm2'):0?>;
@@ -140,6 +144,7 @@ function pauseEvents(id) {
if (!id || i==id) clearTimeout(timer);
});
}
function resumeEvents(id,delay) {
var startDelay = delay||50;
$.each(timers, function(i,timer) {
@@ -147,9 +152,11 @@ function resumeEvents(id,delay) {
startDelay += 50;
});
}
function plus(value,single,plural,last) {
return value>0 ? (value+' '+(value==1?single:plural)+(last?'':', ')) : '';
}
function updateTime() {
var now = new Date();
var days = parseInt(uptime/86400);
@@ -179,6 +186,7 @@ function updateTime() {
}
setTimeout(updateTime,1000);
}
function refresh(top) {
if (typeof top === 'undefined') {
for (var i=0,element; element=document.querySelectorAll('input,button,select')[i]; i++) {element.disabled = true;}
@@ -189,11 +197,13 @@ function refresh(top) {
location.reload();
}
}
function initab(page) {
$.removeCookie('one');
$.removeCookie('tab');
if (page != null) location.replace(page);
}
function settab(tab) {
<?switch ($myPage['name']):?>
<?case'Main':?>
@@ -209,6 +219,7 @@ function settab(tab) {
$.cookie('one',tab);
<?endswitch;?>
}
function done(key) {
var url = location.pathname.split('/');
var path = '/'+url[1];
@@ -216,10 +227,12 @@ function done(key) {
$.removeCookie('one');
location.replace(path);
}
function chkDelete(form, button) {
button.value = form.confirmDelete.checked ? "<?=_('Delete')?>" : "<?=_('Apply')?>";
button.disabled = false;
}
function makeWindow(name,height,width) {
var top = (screen.height-height)/2;
if (top < 0) {top = 0; height = screen.availHeight;}
@@ -227,6 +240,7 @@ function makeWindow(name,height,width) {
if (left < 0) {left = 0; width = screen.availWidth;}
return window.open('',name,'resizeable=yes,scrollbars=yes,height='+height+',width='+width+',top='+top+',left='+left);
}
function openBox(cmd,title,height,width,load,func,id) {
// open shadowbox window (run in foreground)
// included for legacy purposes, replaced by openPlugin
@@ -235,6 +249,7 @@ function openBox(cmd,title,height,width,load,func,id) {
var options = load ? (func ? {modal:true,onClose:function(){setTimeout(func+'('+'"'+(id||'')+'")');}} : {modal:true,onClose:function(){location.reload();}}) : {modal:false};
Shadowbox.open({content:run, player:'iframe', title:title, height:Math.min(screen.availHeight,800), width:Math.min(screen.availWidth,1200), options:options});
}
function openWindow(cmd,title,height,width) {
// open regular window (run in background)
// included for legacy purposes, replaced by openTerminal
@@ -252,6 +267,7 @@ function openWindow(cmd,title,height,width) {
makeWindow(window_name,height,width);
form.submit();
}
function openTerminal(tag,name,more) {
if (/MSIE|Edge/.test(navigator.userAgent)) {
swal({title:"_(Unsupported Feature)_",text:"_(Sorry, this feature is not supported by MSIE/Edge)_.<br>_(Please try a different browser)_",type:'error',html:true,animation:'none',confirmButtonText:"_(Ok)_"});
@@ -263,6 +279,7 @@ function openTerminal(tag,name,more) {
var socket = ['ttyd','syslog'].includes(tag) ? '/webterminal/'+tag+'/' : '/logterminal/'+name+(more=='.log'?more:'')+'/';
$.get('/webGui/include/OpenTerminal.php',{tag:tag,name:name,more:more},function(){setTimeout(function(){tty_window.location=socket; tty_window.focus();},200);});
}
function bannerAlert(text,cmd,plg,func,start) {
$.post('/webGui/include/StartCommand.php',{cmd:cmd,pid:1},function(pid) {
if (pid == 0) {
@@ -299,6 +316,7 @@ function bannerAlert(text,cmd,plg,func,start) {
}
});
}
function openPlugin(cmd,title,plg,func,start=0,button=0) {
// start = 0 : run command only when not already running (default)
// start = 1 : run command unconditionally
@@ -322,6 +340,7 @@ function openPlugin(cmd,title,plg,func,start=0,button=0) {
$('button.confirm').prop('disabled',button!=0);
});
}
function openDocker(cmd,title,plg,func,start=0,button=0) {
// start = 0 : run command only when not already running (default)
// start = 1 : run command unconditionally
@@ -345,6 +364,7 @@ function openDocker(cmd,title,plg,func,start=0,button=0) {
$('button.confirm').prop('disabled',button==0);
});
}
function openVMAction(cmd,title,plg,func,start=0,button=0) {
// start = 0 : run command only when not already running (default)
// start = 1 : run command unconditionally
@@ -368,6 +388,7 @@ function openVMAction(cmd,title,plg,func,start=0,button=0) {
$('button.confirm').prop('disabled',button==0);
});
}
function abortOperation(pid) {
swal({title:"<?=_('Abort background operation')?>",text:"<?=_('This may leave an unknown state')?>",html:true,animation:'none',type:'warning',showCancelButton:true,confirmButtonText:"<?=_('Proceed')?>",cancelButtonText:"<?=_('Cancel')?>"},function(){
$.post('/webGui/include/StartCommand.php',{kill:pid},function() {
@@ -381,6 +402,7 @@ function abortOperation(pid) {
});
});
}
function openChanges(cmd,title,nchan,button=0) {
$('div.spinner.fixed').show();
// button = 0 : hide CLOSE button (default)
@@ -397,6 +419,7 @@ function openChanges(cmd,title,nchan,button=0) {
$('button.confirm').text("<?=_('Done')?>").prop('disabled',false).show();
});
}
function openAlert(cmd,title,func) {
$.post('/webGui/include/StartCommand.php',{cmd:cmd,start:2},function(data) {
$('div.spinner.fixed').hide();
@@ -407,6 +430,7 @@ function openAlert(cmd,title,func) {
$('pre#swalbody').html(data);
});
}
function openDone(data) {
if (data == '_DONE_') {
$('div.spinner.fixed').hide();
@@ -421,6 +445,7 @@ function openDone(data) {
}
return false;
}
function openError(data) {
if (data == '_ERROR_') {
$('div.spinner.fixed').hide();
@@ -429,16 +454,20 @@ function openError(data) {
}
return false;
}
function showStatus(name,plugin,job) {
$.post('/webGui/include/ProcessStatus.php',{name:name,plugin:plugin,job:job},function(status){$(".tabs").append(status);});
}
function showFooter(data, id) {
if (id !== undefined) $('#'+id).remove();
$('#copyright').prepend(data);
}
function showNotice(data) {
$('#user-notice').html(data.replace(/<a>(.*)<\/a>/,"<a href='/Plugins'>$1</a>"));
}
function escapeQuotes(form) {
$(form).find('input[type=text]').each(function(){$(this).val($(this).val().replace(/"/g,'\\"'));});
}
@@ -516,12 +545,14 @@ function removeRebootNotice(message="<?=_('You must reboot for changes to take e
function showUpgradeChanges() { /** @note can likely be removed, not used in webgui or api repos */
openChanges("showchanges /tmp/plugins/unRAIDServer.txt","<?=_('Release Notes')?>");
}
function showUpgrade(text,noDismiss=false) { /** @note can likely be removed, not used in webgui or api repos */
if ($.cookie('os_upgrade')==null) {
if (osUpgradeWarning) removeBannerWarning(osUpgradeWarning);
osUpgradeWarning = addBannerWarning(text.replace(/<a>(.+?)<\/a>/,"<a href='#' onclick='openUpgrade()'>$1</a>").replace(/<b>(.*)<\/b>/,"<a href='#' onclick='document.rebootNow.submit()'>$1</a>"),false,noDismiss);
}
}
function hideUpgrade(set) { /** @note can likely be removed, not used in webgui or api repos */
removeBannerWarning(osUpgradeWarning);
if (set)
@@ -529,6 +560,7 @@ function hideUpgrade(set) { /** @note can likely be removed, not used in webgui
else
$.removeCookie('os_upgrade');
}
function confirmUpgrade(confirm) {
if (confirm) {
swal({title:"<?=_('Update')?> Unraid OS",text:"<?=_('Do you want to update to the new version')?>?",type:'warning',html:true,animation:'none',showCancelButton:true,closeOnConfirm:false,confirmButtonText:"<?=_('Proceed')?>",cancelButtonText:"<?=_('Cancel')?>"},function(){
@@ -538,6 +570,7 @@ function confirmUpgrade(confirm) {
openPlugin("plugin update unRAIDServer.plg","<?=_('Update')?> Unraid OS");
}
}
function openUpgrade() {
hideUpgrade();
$.get('/plugins/dynamix.plugin.manager/include/ShowPlugins.php',{cmd:'alert'},function(data) {
@@ -550,11 +583,13 @@ function openUpgrade() {
}
});
}
function digits(number) {
if (number < 10) return 'one';
if (number < 100) return 'two';
return 'three';
}
function openNotifier() {
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
$.each($.parseJSON(msg), function(i, notify){
@@ -562,6 +597,7 @@ function openNotifier() {
});
});
}
function closeNotifier() {
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
$.each($.parseJSON(msg), function(i, notify){
@@ -570,14 +606,17 @@ function closeNotifier() {
$('div.jGrowl').find('div.jGrowl-close').trigger('click');
});
}
function viewHistory() {
location.replace('/Tools/NotificationsArchive');
}
function flashReport() {
$.post('/webGui/include/Report.php',{cmd:'config'},function(check){
if (check>0) addBannerWarning("<?=_('Your flash drive is corrupted or offline').'. '._('Post your diagnostics in the forum for help').'.'?> <a target='_blank' href='https://docs.unraid.net/go/changing-the-flash-device/'><?=_('See also here')?></a>");
});
}
$(function() {
let tab;
<?switch ($myPage['name']):?>
@@ -710,7 +749,7 @@ unset($buttons,$button);
// Build page content
// Reload page every X minutes during extended viewing?
if (isset($myPage['Load']) && $myPage['Load']>0) echo "\n<script>timers.reload = setTimeout(function(){location.reload();},".($myPage['Load']*60000).");</script>\n";
if (isset($myPage['Load']) && $myPage['Load'] > 0) echo "\n<script>timers.reload = setInterval(function(){if (nchanPaused === false)location.reload();},".($myPage['Load']*60000).");</script>\n";
echo "<div class='tabs'>";
$tab = 1;
$pages = [];
@@ -855,7 +894,7 @@ function parseINI(msg) {
// unraid animated logo
var unraid_logo = '<?readfile("$docroot/webGui/images/animated-logo.svg")?>';
var defaultPage = new NchanSubscriber('/sub/session,var<?=$entity?",notify":""?>',{subscriber:'websocket'});
var defaultPage = new NchanSubscriber('/sub/session,var<?=$entity?",notify":""?>',{subscriber:'websocket', reconnectTimeout:5000});
defaultPage.on('message', function(msg,meta) {
switch (meta.id.channel()) {
case 0:
@@ -916,7 +955,7 @@ function wlanSettings() {
window.location = '/Settings/NetworkSettings';
}
var nchan_wlan0 = new NchanSubscriber('/sub/wlan0',{subscriber:'websocket'});
var nchan_wlan0 = new NchanSubscriber('/sub/wlan0',{subscriber:'websocket', reconnectTimeout:5000});
nchan_wlan0.on('message', function(msg) {
var wlan = JSON.parse(msg);
$('#wlan0').removeClass().addClass(wlan.color).attr('title',wlan.title);
@@ -924,7 +963,7 @@ nchan_wlan0.on('message', function(msg) {
nchan_wlan0.start();
<?endif;?>
var nchan_plugins = new NchanSubscriber('/sub/plugins',{subscriber:'websocket'});
var nchan_plugins = new NchanSubscriber('/sub/plugins',{subscriber:'websocket', reconnectTimeout:5000});
nchan_plugins.on('message', function(data) {
if (!data || openDone(data)) return;
var box = $('pre#swaltext');
@@ -937,7 +976,7 @@ nchan_plugins.on('message', function(data) {
box.html(text.join('<br>')).scrollTop(box[0].scrollHeight);
});
var nchan_docker = new NchanSubscriber('/sub/docker',{subscriber:'websocket'});
var nchan_docker = new NchanSubscriber('/sub/docker',{subscriber:'websocket', reconnectTimeout:5000});
nchan_docker.on('message', function(data) {
if (!data || openDone(data)) return;
var box = $('pre#swaltext');
@@ -986,7 +1025,7 @@ nchan_docker.on('message', function(data) {
box.scrollTop(box[0].scrollHeight);
});
var nchan_vmaction = new NchanSubscriber('/sub/vmaction',{subscriber:'websocket'});
var nchan_vmaction = new NchanSubscriber('/sub/vmaction',{subscriber:'websocket', reconnectTimeout:5000});
nchan_vmaction.on('message', function(data) {
if (!data || openDone(data) || openError(data)) return;
var box = $('pre#swaltext');
@@ -1215,6 +1254,76 @@ $('body').on('click','a,.ca_href', function(e) {
}
}
});
// Start & stop live updates when window loses focus
var nchanPaused = false;
var blurTimer = false;
$(window).focus(function() {
nchanFocusStart();
});
// Stop nchan on loss of focus
<? if ( $display['liveUpdate'] == "no" ):?>
$(window).blur(function() {
blurTimer = setTimeout(function(){
nchanFocusStop();
},30000);
});
<?endif;?>
document.addEventListener("visibilitychange", (event) => {
<? if ( $display['liveUpdate'] == "no" ):?>
if (document.hidden) {
nchanFocusStop();
}
<?else:?>
if (document.hidden) {
nchanFocusStop();
} else {
nchanFocusStart();
}
<?endif;?>
});
function nchanFocusStart() {
if ( blurTimer !== false ) {
clearTimeout(blurTimer);
blurTimer = false;
}
if (nchanPaused !== false ) {
removeBannerWarning(nchanPaused);
nchanPaused = false;
try {
pageFocusFunction();
} catch(error) {}
subscribers.forEach(function(e) {
e.start();
});
}
}
function nchanFocusStop(banner=true) {
if ( subscribers.length ) {
if ( nchanPaused === false ) {
var newsub = subscribers;
subscribers.forEach(function(e) {
try {
e.stop();
} catch(err) {
newsub.splice(newsub.indexOf(e,1));
}
});
subscribers = newsub;
if ( banner && subscribers.length ) {
nchanPaused = addBannerWarning("<?=_('Live Updates Paused');?>",false,true );
}
}
}
}
</script>
<uui-toaster rich-colors close-button position="<?= ($notify['position'] === 'center') ? 'top-center' : $notify['position'] ?>"></uui-toaster>
</body>

View File

@@ -2,9 +2,9 @@ Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
===================================================================
--- /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php original
+++ /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php modified
@@ -556,20 +556,11 @@
return 'three';
@@ -591,20 +591,11 @@
}
function openNotifier() {
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
$.each($.parseJSON(msg), function(i, notify){
@@ -22,9 +22,9 @@ Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
});
});
}
function closeNotifier() {
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
@@ -698,12 +689,12 @@
@@ -737,12 +728,12 @@
}
// create list of nchan scripts to be started
if (isset($button['Nchan'])) nchan_merge($button['root'], $button['Nchan']);
@@ -38,7 +38,7 @@ Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
foreach ($buttons as $button) {
annotate($button['file']);
// include page specific stylesheets (if existing)
@@ -905,26 +896,18 @@
@@ -944,26 +935,18 @@
case 'warning': bell2++; break;
case 'normal' : bell3++; break;
}
@@ -70,11 +70,11 @@ Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
});
<?if ($wlan0):?>
@@ -1231,7 +1214,8 @@
});
@@ -1340,7 +1323,8 @@
}
}
}
});
}
</script>
+<uui-toaster rich-colors close-button position="<?= ($notify['position'] === 'center') ? 'top-center' : $notify['position'] ?>"></uui-toaster>
</body>