feat(web): update os create flash backup button

This commit is contained in:
Zack Spear
2023-10-02 15:00:34 -07:00
committed by Zack Spear
parent cadbd65cf6
commit bfa667c1ab
17 changed files with 439 additions and 53 deletions
+4
View File
@@ -311,10 +311,12 @@ if [ -e /etc/rc.d/rc.unraid-api ]; then
# restore stock files
FILE=/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix/Registration.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix/Update.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix/DisplaySettings.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/MyServers.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/Registration.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/Update.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/myservers1.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/myservers2.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/state.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
@@ -402,8 +404,10 @@ echo
# Preserve in case plugin is removed
FILE=/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix/Registration.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix/Update.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/MyServers.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/Registration.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/Update.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/myservers1.php && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/myservers2.php && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/state.php && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
@@ -0,0 +1,162 @@
Menu="About"
Title="Update OS"
Icon="icon-update"
Tag="upload"
---
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2012-2022, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* If /boot/previous/bzroot exists, then the user has the option to downgrade to the previous version.
* Parse the text file /boot/previous/changes.txt to get the version number of the previous version.
* Then we move some file around and reboot.
*/
?>
<?
$check = $notify['unraidos'] ? 0 : 1;
$restoreVersion = $restoreBranch = $restoreVersionReleaseDate = 'unknown';
$restoreExists = file_exists('/boot/previous/bzroot');
$restoreChangelogPath = '/boot/previous/changes.txt';
$diagnosticsZip = htmlspecialchars(str_replace(' ', '_', strtolower($var['NAME'])));
if (file_exists($restoreChangelogPath)) {
exec("head -n4 $restoreChangelogPath", $rows);
foreach ($rows as $row) {
$i = stripos($row,'version');
if ($i !== false) {
[$restoreVersion, $restoreVersionReleaseDate] = explode(' ', trim(substr($row, $i+7)));
break;
}
}
$restoreBranch = strpos($restoreVersion, 'rc') !== false
? _('Next')
: (strpos($restoreVersion, 'beta') !== false
? _('Beta')
: _('Stable'));
}
?>
<unraid-i18n-host>
<unraid-update-os
restore-version="<?= $restoreExists && $restoreVersion != 'unknown' ? $restoreVersion : '' ?>"
restore-release-date="<?= $restoreExists && $restoreVersionReleaseDate != 'unknown' ? $restoreVersionReleaseDate : '' ?>"></unraid-update-os>
</unraid-i18n-host>
<script>
var diagnosticsFile = "";
var nchan_diagnostics = new NchanSubscriber('/sub/diagnostics', { subscriber: 'websocket' });
const args = {};
nchan_diagnostics.on('message', function(data) {
if (data == '_DONE_') {
nchan_diagnostics.stop();
$('.sweet-alert').hide('fast').removeClass('nchan');
swal.close();
location = diagnosticsFile;
setTimeout(cleanUp,4000);
} else if (data) {
let box = $('pre#swaltext');
box.html(box.html() + '<br>' + data).scrollTop(box[0].scrollHeight);
}
});
function downgradeAction() {
$.get('/plugins/dynamix.plugin.manager/include/Downgrade.php', { version: '<?=$restoreVersion?>' }, function() { refresh(); });
}
function downgrade() {
swal({
title: "_(Diagnostics)_",
text: "_(Please provide diagnostics when experiencing problems)_<br>_(Post these in the forums)_",
html: true,
type: 'warning',
showCancelButton: true,
confirmButtonText: "<?= _('Diagnostics') ?>",
cancelButtonText: "<?= _('Restore') ?>",
}, function(diag) {
if (diag) {
// get diagnostics and then downgrade
setTimeout(function() {
diagnostics(zipfile());
}, 250);
} else {
// downgrade immediately
downgradeAction();
}
});
}
function cleanUp() {
if (document.hasFocus()) {
$.post('/webGui/include/Download.php', { cmd: 'delete', file: diagnosticsFile }, downgradeAction());
} else {
setTimeout(cleanUp,2000);
}
}
function zipfile() {
const tzoffset = (new Date()).getTimezoneOffset() * 60000; // offset in milliseconds
const localISOTime = (new Date(Date.now() - tzoffset));
const year = localISOTime.getFullYear();
const month = String(localISOTime.getMonth() + 1).padStart(2, '0');
const day = String(localISOTime.getDate()).padStart(2, '0');
const hours = String(localISOTime.getHours()).padStart(2, '0');
const minutes = String(localISOTime.getMinutes()).padStart(2, '0');
const dateOutput = `${year}${month}${day}_${hours}${minutes}`;
return '<?=$diagnosticsZip?>-diagnostics-' + dateOutput + '.zip';
}
function diagnostics(file) {
nchan_diagnostics.start();
$.post('/webGui/include/Download.php', { cmd:'diag', file: file, anonymize: '' }, function(zip) {
if (zip) {
diagnosticsFile = zip;
swal({
title: "_(Downloading)_...",
text: "/boot/logs" + zip + "<hr><pre id='swaltext'></pre>",
html: true,
animation: 'none',
showConfirmButton: false,
});
$('.sweet-alert').addClass('nchan');
$('button.confirm').prop('disabled', true);
} else {
nchan_diagnostics.stop();
}
});
}
function cleanUpFlashBackup(zip) {
if (document.hasFocus()) {
$('input[value="_(Creating Flash backup)_..."]').val("_(Flash backup)_").prop('disabled',false);
$('div.spinner').hide('slow');
$('#pleaseWait').hide('slow');
$.post('/webGui/include/Download.php',{cmd:'unlink',file:zip});
} else {
setTimeout(function(){cleanUpFlashBackup(zip);},2000);
}
}
function flashBackup() {
$('input[value="_(Flash backup)_"]').val('_(Creating Flash backup)_...').prop('disabled',true);
$('div.spinner').show('slow');
$('#pleaseWait').show('slow');
$.post('/webGui/include/Download.php',{cmd:'backup'},function(zip) {
if (zip) {
location = '/'+zip;
setTimeout(function(){cleanUpFlashBackup(zip);},6000);
} else {
$('input[value="_(Creating Flash backup)_..."]').val("_(Flash backup)_");
$('div.spinner').hide('slow');
$('#pleaseWait').hide('slow');
swal({title:"_(Creation error)_",text:"_(Insufficient free disk space available)_",type:'error',html:true,confirmButtonText:"_(Ok)_"});
}
});
}
</script>
+8 -8
View File
@@ -29,7 +29,7 @@ const randomGuid = `1111-1111-${makeid(4)}-123412341234`; // this guid is regist
// EBLACKLISTED1
// EBLACKLISTED2
// ENOCONN
const state: ServerState = 'ENOKEYFILE';
const state: ServerState = 'STARTER';
let regDev = 0;
let regTy = '';
switch (state) {
@@ -68,8 +68,8 @@ else if (state === 'EEXPIRED') { expireTime = uptime; } // 1 hour ago
let regExp: number | undefined = undefined;
if (state === 'STARTER' || state === 'UNLEASHED') {
// regExp = oneHourFromNow;
regExp = uptime;
regExp = oneHourFromNow;
// regExp = uptime;
}
export const serverState: Server = {
@@ -79,11 +79,12 @@ export const serverState: Server = {
// error: 'INVALID',
valid: true,
},
// connectPluginInstalled: 'dynamix.unraid.net.staging.plg',
connectPluginInstalled: '',
connectPluginInstalled: 'dynamix.unraid.net.staging.plg',
// connectPluginInstalled: '',
description: 'DevServer9000',
deviceCount: 12,
deviceCount: 3,
expireTime,
flashBackupActivated: true,
flashProduct: 'SanDisk_3.2Gen1',
flashVendor: 'USB',
guid: randomGuid,
@@ -94,13 +95,12 @@ export const serverState: Server = {
license: '',
locale: 'en_US', // en_US, ja
name: 'fuji',
osVersion: '6.12.3-beta25',
osVersion: '6.12.5',
registered: true,
regGen: 0,
regTm: uptime,
regTo: 'Zack Spear',
regTy,
// regExp: oneHourFromNow,
regExp,
// "regGuid": "0781-5583-8355-81071A2B0211",
site: 'http://localhost:4321',
+7 -7
View File
@@ -33,7 +33,7 @@ defineEmits(['click']);
const classes = computed(() => {
/** @todo consider underline for all buttons to improve accessibility and quick readability */
const buttonDefaults = 'group text-center font-semibold relative z-0 flex flex-row items-center justify-center cursor-pointer rounded-md shadow-none hover:shadow-md focus:shadow-md disabled:opacity-50 disabled:hover:opacity-50 disabled:focus:opacity-50 disabled:cursor-not-allowed';
const buttonDefaults = 'group text-center font-semibold leading-none relative z-0 flex flex-row items-center justify-center cursor-pointer rounded-md shadow-none hover:shadow-md focus:shadow-md disabled:opacity-50 disabled:hover:opacity-50 disabled:focus:opacity-50 disabled:cursor-not-allowed';
let buttonColors = '';
let buttonSize = '';
let iconSize = '';
@@ -58,27 +58,27 @@ const classes = computed(() => {
switch (props.size) {
case '12px':
buttonSize = 'text-12px px-8px py-4px gap-4px';
buttonSize = 'text-12px p-8px gap-4px';
iconSize = 'w-12px';
break;
case '14px':
buttonSize = 'text-14px px-8px py-4px gap-8px';
buttonSize = 'text-14px p-8px gap-8px';
iconSize = 'w-14px';
break;
case '16px':
buttonSize = 'text-16px px-12px py-8px gap-8px';
buttonSize = 'text-16px p-12px gap-8px';
iconSize = 'w-16px';
break;
case '18px':
buttonSize = 'text-18px px-12px py-8px gap-8px';
buttonSize = 'text-18px p-12px gap-8px';
iconSize = 'w-18px';
break;
case '20px':
buttonSize = 'text-20px px-16px py-12px gap-8px';
buttonSize = 'text-20px p-16px gap-8px';
iconSize = 'w-20px';
break;
case '24px':
buttonSize = 'text-24px px-16px py-12px gap-8px';
buttonSize = 'text-24px p-16px gap-8px';
iconSize = 'w-24px';
break;
}
+2 -2
View File
@@ -3,7 +3,7 @@ import { storeToRefs } from 'pinia';
import { ArrowDownTrayIcon, ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
import { useI18n } from 'vue-i18n';
import { CONNECT_FORUMS, CONTACT, DISCORD, GRAPHQL } from '~/helpers/urls';
import { CONNECT_FORUMS, CONTACT, DISCORD, WEBGUI_GRAPHQL } from '~/helpers/urls';
import { useServerStore } from '~/store/server';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
@@ -12,7 +12,7 @@ const { t } = useI18n();
const { apiKey } = storeToRefs(useServerStore());
const downloadUrl = computed(() => new URL(`/graphql/api/logs?apiKey=${apiKey.value}`, GRAPHQL));
const downloadUrl = computed(() => new URL(`/graphql/api/logs?apiKey=${apiKey.value}`, WEBGUI_GRAPHQL));
</script>
<template>
+7 -1
View File
@@ -10,6 +10,7 @@ import { useI18n } from 'vue-i18n';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { WEBGUI_TOOLS_UPDATE } from '~/helpers/urls';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore, useUpdateOsActionsStore } from '~/store/updateOsActions';
@@ -45,7 +46,12 @@ const showRebootRequired = computed(() => rebootType.value !== '');
</UiBadge>
</button>
<a v-if="showUpdateAvailable || showRebootRequired" href="/Tools/Update" class="group" :title="t('Go to Tools > Update')">
<a
v-if="showUpdateAvailable || showRebootRequired"
:href="WEBGUI_TOOLS_UPDATE.toString()"
class="group"
:title="t('Go to Tools > Update')"
>
<UiBadge
v-if="showUpdateAvailable"
color="orange"
+59
View File
@@ -0,0 +1,59 @@
<script setup lang="ts">
/**
* @todo complete this component
*/
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
const props = withDefaults(defineProps<{
description?: string; // @todo setup
label: string;
}>(), {
description: '',
});
const checked = ref(false);
</script>
<template>
<SwitchGroup as="div">
<div class="flex flex-shrink-0 items-center gap-16px">
<Switch
v-model="checked"
:class="[
checked ? 'bg-green-500' : 'bg-gray-200',
'relative inline-flex h-24px w-[44px] flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2',
]"
>
<span
:class="[
checked ? 'translate-x-20px' : 'translate-x-0',
'pointer-events-none relative inline-block h-20px w-20px transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
]"
>
<span
:class="[
checked ? 'opacity-0 duration-100 ease-out' : 'opacity-100 duration-200 ease-in',
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity',
]"
aria-hidden="true"
>
<svg class="h-12px w-12px text-gray-400" fill="none" viewBox="0 0 12 12">
<path d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</span>
<span
:class="[
checked ? 'opacity-100 duration-200 ease-in' : 'opacity-0 duration-100 ease-out',
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity',
]"
aria-hidden="true"
>
<svg class="h-12px w-12px text-green-500" fill="currentColor" viewBox="0 0 12 12">
<path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" />
</svg>
</span>
</span>
</Switch>
<SwitchLabel class="text-14px">{{ label }}</SwitchLabel>
</div>
</SwitchGroup>
</template>
+132 -10
View File
@@ -4,7 +4,9 @@
* @todo require keyfile to update
* @todo require valid guid / server state to update
*/
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
import {
ArchiveBoxArrowDownIcon,
ArrowPathIcon,
ArrowSmallRightIcon,
ArrowTopRightOnSquareIcon,
@@ -18,6 +20,7 @@ import { ref, watchEffect } from 'vue';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore, useUpdateOsActionsStore } from '~/store/updateOsActions';
import type { UserProfileLink } from '~/types/userProfile';
@@ -28,6 +31,7 @@ const props = defineProps<{
const updateOsStore = useUpdateOsStore();
const updateOsActionsStore = useUpdateOsActionsStore();
const { connectPluginInstalled, flashBackupActivated } = storeToRefs(useServerStore());
const { available } = storeToRefs(updateOsStore);
const { ineligibleText } = storeToRefs(updateOsActionsStore);
@@ -57,12 +61,70 @@ const subheading = computed(() => {
return '';
});
const flashBackupCopy = computed(() => {
const base = props.t('We recommend backing up your USB Flash Boot Device before starting the update.');
if (connectPluginInstalled.value && flashBackupActivated.value) {
return `${base}
${props.t('You have already activated the Flash Backup feature via the Unraid Connect plugin.')}
${props.t('Go to Tools > Management Access to ensure your backup is up-to-date.')}
${props.t('You can also manually create a new backup by clicking the Create Flash Backup button.')}
`;
}
if (connectPluginInstalled.value && !flashBackupActivated.value) {
return `${base}
${props.t('You have not activated the Flash Backup feature via the Unraid Connect plugin.')}
${props.t('Go to Tools > Management Access to activate the Flash Backup feature and ensure your backup is up-to-date.')}
${props.t('You can also manually create a new backup by clicking the Create Flash Backup button.')}
`;
}
return `${base} ${props.t('You can manually create a backup by clicking the Create Flash Backup button.')}`
});
const acknowledgeBackup = ref<boolean>(false);
const flashBackupBasicStatus = ref<'complete' | 'ready' | 'started'>('ready');
const flashBackupText = computed(() => props.t('Create Flash Backup'));
const startFlashBackup = () => {
console.debug('[startFlashBackup]', Date.now());
// @ts-ignore global function provided by the webgui on the update page
if (typeof flashBackup === 'function') {
flashBackupBasicStatus.value = 'started';
// @ts-ignore global function provided by the webgui on the update page
flashBackup();
checkFlashBackupStatus();
} else {
alert(props.t('Flash Backup is not available. Navigate to {0}/Main/Settings/Flash to try again then come back to this page.', [window.location.origin]));
}
};
/**
* Checking for element on the page to determine if the flash backup has started
*/
const checkFlashBackupStatus = () => {
let loadingElement: HTMLCollectionOf<Element> = document.getElementsByClassName('spinner');
setTimeout(() => {
if (loadingElement.length > 0 && loadingElement[0]) {
const el = loadingElement[0] as HTMLDivElement;
const loaderHidden = el.style.display === 'none';
if (loaderHidden) {
flashBackupBasicStatus.value = 'complete';
console.debug('[checkFlashBackupStatus] complete', Date.now());
} else {
checkFlashBackupStatus(); // check again
}
} else {
flashBackupBasicStatus.value = 'complete';
}
}, 500);
};
watchEffect(() => {
if (available.value) {
updateButton.value = updateOsActionsStore.initUpdateOsCallback();
} else {
updateButton.value = undefined;
}
if (flashBackupBasicStatus.value === 'complete') {
acknowledgeBackup.value = true; // auto check the box
}
});
</script>
@@ -79,9 +141,12 @@ watchEffect(() => {
<h4 v-if="subheading" class="text-18px font-semibold leading-normal">
{{ subheading }}
</h4>
<div class="text-16px leading-relaxed whitespace-normal" :class="!ineligibleText ? 'opacity-75' : ''">
<div class="prose text-16px leading-relaxed whitespace-normal" :class="!ineligibleText ? 'opacity-75' : ''">
<p v-if="ineligibleText">{{ ineligibleText }}</p>
<p v-else>{{ t('Receive the latest and greatest for Unraid OS. Whether it new features, security patches, or bug fixes keeping your server up-to-date ensures the best experience that Unraid has to offer.') }}</p>
<template v-else>
<p>{{ t('Receive the latest and greatest for Unraid OS. Whether it new features, security patches, or bug fixes keeping your server up-to-date ensures the best experience that Unraid has to offer.') }}</p>
<p v-if="available">{{ flashBackupCopy }}</p>
</template>
</div>
</div>
@@ -94,14 +159,71 @@ watchEffect(() => {
:text="t('Learn more and fix')"
class="flex-none"
/>
<BrandButton
v-else-if="available && updateButton"
@click="updateButton?.click"
:external="updateButton?.external"
:icon-right="ArrowTopRightOnSquareIcon"
:name="updateButton?.name"
:text="t('View changelog & update')"
class="flex-none" />
<div v-else-if="available && updateButton" class="flex flex-col sm:flex-shrink-0 items-center gap-16px">
<BrandButton
@click="startFlashBackup"
btn-style="outline"
:disabled="flashBackupBasicStatus === 'started'"
:icon="ArchiveBoxArrowDownIcon"
:name="'flashBackup'"
:text="flashBackupText"
class="flex-none" />
<p v-if="flashBackupBasicStatus === 'started'" class="text-12px italic opacity-75 shrink">{{ t('Backing up...this may take a few minutes. Please wait for the download to complete before starting the update.') }}</p>
<SwitchGroup as="div">
<div class="flex flex-shrink-0 items-center gap-16px">
<Switch
v-model="acknowledgeBackup"
:class="[
acknowledgeBackup ? 'bg-green-500' : 'bg-gray-200',
'relative inline-flex h-24px w-[44px] flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2',
]"
>
<span
:class="[
acknowledgeBackup ? 'translate-x-20px' : 'translate-x-0',
'pointer-events-none relative inline-block h-20px w-20px transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
]"
>
<span
:class="[
acknowledgeBackup ? 'opacity-0 duration-100 ease-out' : 'opacity-100 duration-200 ease-in',
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity',
]"
aria-hidden="true"
>
<svg class="h-12px w-12px text-gray-400" fill="none" viewBox="0 0 12 12">
<path d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</span>
<span
:class="[
acknowledgeBackup ? 'opacity-100 duration-200 ease-in' : 'opacity-0 duration-100 ease-out',
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity',
]"
aria-hidden="true"
>
<svg class="h-12px w-12px text-green-500" fill="currentColor" viewBox="0 0 12 12">
<path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" />
</svg>
</span>
</span>
</Switch>
<SwitchLabel class="text-14px">{{ t('I have made a Flash Backup') }}</SwitchLabel>
</div>
</SwitchGroup>
<BrandButton
@click="updateButton?.click"
:disabled="!acknowledgeBackup"
:external="updateButton?.external"
:icon-right="ArrowTopRightOnSquareIcon"
:name="updateButton?.name"
:text="t('View Changelog & Update')"
:title="!acknowledgeBackup ? t('Acklowledge that you have made a Flash Backup to enable this action') : ''"
class="flex-none" />
</div>
</div>
</UiCardWrapper>
</template>
@@ -6,7 +6,7 @@ import { ChevronDoubleDownIcon, ClipboardIcon, CogIcon } from '@heroicons/vue/24
import { storeToRefs } from 'pinia';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { PLUGIN_SETTINGS } from '~/helpers/urls';
import { WEBGUI_CONNECT_SETTINGS } from '~/helpers/urls';
import { useAccountStore } from '~/store/account';
import { useCallbackActionsStore } from '~/store/callbackActions';
import { useInstallKeyStore } from '~/store/installKey';
@@ -300,7 +300,7 @@ const { copy, copied, isSupported } = useClipboard({ source: keyUrl.value });
/>
<BrandButton
v-else
:href="PLUGIN_SETTINGS.toString()"
:href="WEBGUI_CONNECT_SETTINGS.toString()"
:icon="CogIcon"
:text="t('Configure Connect Features')"
class="grow-0"
@@ -2,7 +2,7 @@
import { storeToRefs } from 'pinia';
import { ArrowTopRightOnSquareIcon, CogIcon } from '@heroicons/vue/24/solid';
import { ACCOUNT, CONNECT_DASHBOARD, PLUGIN_SETTINGS } from '~/helpers/urls';
import { ACCOUNT, CONNECT_DASHBOARD, WEBGUI_CONNECT_SETTINGS } from '~/helpers/urls';
import { useErrorsStore } from '~/store/errors';
// import { usePromoStore } from '~/store/promo';
import { useServerStore } from '~/store/server';
@@ -18,7 +18,7 @@ const updateOsActionsStore = useUpdateOsActionsStore();
const { errors } = storeToRefs(errorsStore);
const { keyActions, connectPluginInstalled, registered, stateData } = storeToRefs(useServerStore());
const { available: osUpdateAvailable } = storeToRefs(useUpdateOsStore());
const { rebootType } = storeToRefs(updateOsActionsStore);
const { rebootType, toolsRegistrationAction } = storeToRefs(updateOsActionsStore);
const signInAction = computed(() => stateData.value.actions?.filter((act: { name: string; }) => act.name === 'signIn') ?? []);
const signOutAction = computed(() => stateData.value.actions?.filter((act: { name: string; }) => act.name === 'signOut') ?? []);
@@ -43,7 +43,7 @@ const links = computed(():UserProfileLink[] => {
title: props.t('Manage Unraid.net Account in new tab'),
},
{
href: PLUGIN_SETTINGS.toString(),
href: WEBGUI_CONNECT_SETTINGS.toString(),
icon: CogIcon,
text: props.t('Settings'),
title: props.t('Go to Connect plugin settings'),
@@ -97,7 +97,7 @@ const showKeyline = computed(() => showConnectStatus.value && (keyActions.value?
<template v-if="osUpdateAvailable && !rebootType">
<li>
<UpcDropdownItem :item="updateOsActionsStore.initUpdateOsCallback()" :t="t" />
<UpcDropdownItem :item="toolsRegistrationAction" :t="t" />
</li>
</template>
+1 -1
View File
@@ -1,7 +1,7 @@
export const preventClose = (e: { preventDefault: () => void; returnValue: string; }) => {
e.preventDefault();
e.returnValue = '';
alert('Closing this pop-up window while actions are being preformed may lead to unintended errors.');
confirm('Closing this pop-up window while actions are being preformed may lead to unintended errors.');
};
export const addPreventClose = () => {
+10 -6
View File
@@ -10,11 +10,13 @@ const CONNECT_DASHBOARD = new URL(import.meta.env.VITE_CONNECT ?? 'https://conne
const CONNECT_FORUMS = new URL('/forum/94-connect-plugin-support/', FORUMS);
const CONTACT = new URL('/contact', UNRAID_NET);
const DISCORD = new URL('https://discord.gg/unraid');
const GRAPHQL = new URL('/graphql', import.meta.env.VITE_GRAPHQL ?? window.location.origin);
const PURCHASE_CALLBACK = new URL('/c', UNRAID_NET);
const SETTINGS_MANAGMENT_ACCESS = new URL('/Settings/ManagementAccess', window.location.origin);
const PLUGIN_SETTINGS = new URL('#UnraidNetSettings', SETTINGS_MANAGMENT_ACCESS);
const WEBGUI = new URL(import.meta.env.VITE_WEBGUI ?? window.location.origin);
const WEBGUI_GRAPHQL = new URL('/graphql', WEBGUI);
const WEBGUI_SETTINGS_MANAGMENT_ACCESS = new URL('/Settings/ManagementAccess', WEBGUI);
const WEBGUI_CONNECT_SETTINGS = new URL('#UnraidNetSettings', WEBGUI_SETTINGS_MANAGMENT_ACCESS);
const WEBGUI_TOOLS_UPDATE = new URL('/Tools/Update', WEBGUI);
const OS_RELEASES = new URL('https://s3.amazonaws.com/dnld.lime-technology.com/stable/releases.json');
@@ -31,11 +33,13 @@ export {
DISCORD,
FORUMS,
FORUMS_BUG_REPORT,
GRAPHQL,
PURCHASE_CALLBACK,
PLUGIN_SETTINGS,
SETTINGS_MANAGMENT_ACCESS,
OS_RELEASES,
DOCS_REGISTRATION_LICENSING,
DOCS_REGISTRATION_REPLACE_KEY,
WEBGUI,
WEBGUI_CONNECT_SETTINGS,
WEBGUI_GRAPHQL,
WEBGUI_SETTINGS_MANAGMENT_ACCESS,
WEBGUI_TOOLS_UPDATE,
};
+15 -2
View File
@@ -226,7 +226,7 @@
"Checking...": "Checking...",
"View release notes": "View release notes",
"View Changelog for {0}": "View Changelog for {0}",
"View changelog & update": "View changelog & update",
"View Changelog & Update": "View Changelog & Update",
"{0} Release Notes": "{0} Release Notes",
"Unable to open release notes": "Unable to open release notes",
"Downgrades are only recommended if you're unable to solve a critical issue. In the rare event you need to downgrade we ask that you please provide us with Diagnostics so we can investigate your issue. You will be prompted with the option to download the Diagnostics zip once the downgrade process is started. From there please open a bug report on our forums with a description of the issue and include your diagnostics.": "Downgrades are only recommended if you're unable to solve a critical issue. In the rare event you need to downgrade we ask that you please provide us with Diagnostics so we can investigate your issue. You will be prompted with the option to download the Diagnostics zip once the downgrade process is started. From there please open a bug report on our forums with a description of the issue and include your diagnostics.",
@@ -287,5 +287,18 @@
"Expires at {0}": "Expires at {0}",
"Expires in {0}": "Expires in {0}",
"Expired": "Expired",
"Expired {0}": "Expired {0}"
"Expired {0}": "Expired {0}",
"Create Flash Backup": "Create Flash Backup",
"Get a Lifetime Key" : "Get a Lifetime Key",
"We recommend backing up your USB Flash Boot Device before starting the update.": "We recommend backing up your USB Flash Boot Device before starting the update.",
"You have already activated the Flash Backup feature via the Unraid Connect plugin.": "You have already activated the Flash Backup feature via the Unraid Connect plugin.",
"Go to Tools > Management Access to ensure your backup is up-to-date.": "Go to Tools > Management Access to ensure your backup is up-to-date.",
"You have not activated the Flash Backup feature via the Unraid Connect plugin.": "You have not activated the Flash Backup feature via the Unraid Connect plugin.",
"Go to Tools > Management Access to activate the Flash Backup feature and ensure your backup is up-to-date.": "Go to Tools > Management Access to activate the Flash Backup feature and ensure your backup is up-to-date.",
"Flash Backup is not available. Navigate to {0}/Main/Settings/Flash to try again then come back to this page.": "Flash Backup is not available. Navigate to {0}/Main/Settings/Flash to try again then come back to this page.",
"Backing up...this may take a few minutes. Please wait for the download to complete before starting the update.": "Backing up...this may take a few minutes. Please wait for the download to complete before starting the update.",
"Acklowledge that you have made a Flash Backup to enable this action": "Acklowledge that you have made a Flash Backup to enable this action",
"You can also manually create a new backup by clicking the Create Flash Backup button.": "You can also manually create a new backup by clicking the Create Flash Backup button.",
"You can manually create a backup by clicking the Create Flash Backup button.": "You can manually create a backup by clicking the Create Flash Backup button.",
"I have made a Flash Backup": "I have made a Flash Backup"
}
+8 -5
View File
@@ -16,7 +16,7 @@ import { useQuery } from '@vue/apollo-composable';
import { SERVER_STATE_QUERY } from './server.fragment';
import type { serverStateQuery } from '~/composables/gql/graphql';
import { WebguiState } from '~/composables/services/webgui';
import { SETTINGS_MANAGMENT_ACCESS } from '~/helpers/urls';
import { WEBGUI_SETTINGS_MANAGMENT_ACCESS } from '~/helpers/urls';
import { useAccountStore } from '~/store/account';
import { useErrorsStore, type Error } from '~/store/errors';
import { usePurchaseStore } from '~/store/purchase';
@@ -74,6 +74,7 @@ export const useServerStore = defineStore('server', () => {
const deviceCount = ref<number>(0);
const email = ref<string>('');
const expireTime = ref<number>(0);
const flashBackupActivated = ref<boolean>(false);
const flashProduct = ref<string>('');
const flashVendor = ref<string>('');
const guid = ref<string>('');
@@ -391,7 +392,7 @@ export const useServerStore = defineStore('server', () => {
],
humanReadable: state.value === 'BASIC' ? 'Basic' : 'Starter',
heading: 'Thank you for choosing Unraid OS!',
message: registered.value
message: registered.value && connectPluginInstalled.value
? '<p>Register for Connect by signing in to your Unraid.net account</p>'
: guidRegistered.value
? '<p>To support more storage devices as your server grows, click Upgrade Key.</p>'
@@ -406,7 +407,7 @@ export const useServerStore = defineStore('server', () => {
],
humanReadable: 'Plus',
heading: 'Thank you for choosing Unraid OS!',
message: registered.value
message: registered.value && connectPluginInstalled.value
? '<p>Register for Connect by signing in to your Unraid.net account</p>'
: guidRegistered.value
? '<p>To support more storage devices as your server grows, click Upgrade Key.</p>'
@@ -425,7 +426,7 @@ export const useServerStore = defineStore('server', () => {
? 'Pro'
: (state.value === 'LIFETIME' ? 'Lifetime' : 'Unleashed'),
heading: 'Thank you for choosing Unraid OS!',
message: registered.value
message: registered.value && connectPluginInstalled.value
? '<p>Register for Connect by signing in to your Unraid.net account</p>'
: '',
};
@@ -645,7 +646,7 @@ export const useServerStore = defineStore('server', () => {
? {
actions: [
{
href: SETTINGS_MANAGMENT_ACCESS.toString(),
href: WEBGUI_SETTINGS_MANAGMENT_ACCESS.toString(),
icon: CogIcon,
text: 'Go to Management Access Now',
},
@@ -730,6 +731,7 @@ export const useServerStore = defineStore('server', () => {
if (typeof data?.deviceCount !== 'undefined') { deviceCount.value = data.deviceCount; }
if (typeof data?.email !== 'undefined') { email.value = data.email; }
if (typeof data?.expireTime !== 'undefined') { expireTime.value = data.expireTime; }
if (typeof data?.flashBackupActivated !== 'undefined') { flashBackupActivated.value = data.flashBackupActivated; }
if (typeof data?.flashProduct !== 'undefined') { flashProduct.value = data.flashProduct; }
if (typeof data?.flashVendor !== 'undefined') { flashVendor.value = data.flashVendor; }
if (typeof data?.guid !== 'undefined') { guid.value = data.guid; }
@@ -878,6 +880,7 @@ export const useServerStore = defineStore('server', () => {
description,
deviceCount,
expireTime,
flashBackupActivated,
flashProduct,
flashVendor,
guid,
+4 -4
View File
@@ -11,7 +11,7 @@ import { defineStore, createPinia, setActivePinia } from 'pinia';
import { UserProfileLink } from 'types/userProfile';
import { WebguiUnraidApiCommand } from '~/composables/services/webgui';
import { GRAPHQL, SETTINGS_MANAGMENT_ACCESS } from '~/helpers/urls';
import { WEBGUI_GRAPHQL, WEBGUI_SETTINGS_MANAGMENT_ACCESS } from '~/helpers/urls';
import { useErrorsStore } from '~/store/errors';
import { useServerStore } from '~/store/server';
@@ -24,8 +24,8 @@ setActivePinia(createPinia());
const ERROR_CORS_403 = 'The CORS policy for this site does not allow access from the specified Origin';
let prioritizeCorsError = false; // Ensures we don't overwrite this specific error message with a non-descriptive network error message
const httpEndpoint = GRAPHQL;
const wsEndpoint = new URL(GRAPHQL.toString().replace('http', 'ws'));
const httpEndpoint = WEBGUI_GRAPHQL;
const wsEndpoint = new URL(WEBGUI_GRAPHQL.toString().replace('http', 'ws'));
export const useUnraidApiStore = defineStore('unraidApi', () => {
const errorsStore = useErrorsStore();
@@ -104,7 +104,7 @@ export const useUnraidApiStore = defineStore('unraidApi', () => {
type: 'unraidApiGQL',
actions: [
{
href: `${SETTINGS_MANAGMENT_ACCESS.toString()}#extraOriginsSettings`,
href: `${WEBGUI_SETTINGS_MANAGMENT_ACCESS.toString()}#extraOriginsSettings`,
icon: CogIcon,
text: 'Go to Connect Settings',
}
+13 -1
View File
@@ -3,7 +3,7 @@ import { defineStore, createPinia, setActivePinia } from 'pinia';
import useInstallPlugin from '~/composables/installPlugin';
import { ACCOUNT_CALLBACK } from '~/helpers/urls';
import { ACCOUNT_CALLBACK, WEBGUI_TOOLS_UPDATE } from '~/helpers/urls';
import { useCallbackStore } from '~/store/callbackActions';
import { useErrorsStore } from '~/store/errors';
@@ -68,6 +68,17 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
return '';
});
const toolsRegistrationAction = computed(() => {
return {
href: WEBGUI_TOOLS_UPDATE.toString(),
emphasize: true,
icon: BellAlertIcon,
name: 'updateOs',
text: 'Unraid OS {0} Update Available',
textParams: [updateOsStore.available],
}
});
// Actions
const initUpdateOsCallback = (includeNextReleases: boolean = false): UserProfileLink => {
return {
@@ -160,6 +171,7 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
status,
ineligible,
ineligibleText,
toolsRegistrationAction,
// Actions
confirmUpdateOs,
installOsUpdate,
+1
View File
@@ -51,6 +51,7 @@ export interface Server {
deviceCount?: number;
email?: string;
expireTime?: number;
flashBackupActivated?: boolean;
flashProduct?: string;
flashVendor?: string;
guid?: string;