mirror of
https://github.com/unraid/api.git
synced 2026-01-03 23:19:54 -06:00
feat: contact support using webgui feedback modal
This commit is contained in:
@@ -29,7 +29,7 @@ const blacklistedGuid = '154B-00EE-0700-9B50CF819816';
|
||||
// EBLACKLISTED1
|
||||
// EBLACKLISTED2
|
||||
// ENOCONN
|
||||
const state: string = 'BASIC';
|
||||
const state: string = 'EGUID';
|
||||
|
||||
const uptime = Date.now() - 60 * 60 * 1000; // 1 hour ago
|
||||
let expireTime = 0;
|
||||
@@ -55,7 +55,7 @@ const serverState = {
|
||||
"license": "",
|
||||
"locale": "en_US",
|
||||
"name": "fuji",
|
||||
"pluginInstalled": "dynamix.unraid.net.staging.plg",
|
||||
"connectPluginInstalled": "dynamix.unraid.net.staging.plg",
|
||||
"registered": false,
|
||||
"regGen": 0,
|
||||
// "regGuid": "0781-5583-8355-81071A2B0211",
|
||||
|
||||
@@ -54,21 +54,21 @@ $flashbackup_status = (file_exists($flashbackup_ini)) ? @parse_ini_file($flashba
|
||||
|
||||
$nginx = parse_ini_file('/var/local/emhttp/nginx.ini');
|
||||
|
||||
$pluginInstalled = '';
|
||||
$connectPluginInstalled = '';
|
||||
if (!file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net') && !file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net.staging')) {
|
||||
$pluginInstalled = ''; // base OS only, plugin not installed • show ad for plugin
|
||||
$connectPluginInstalled = ''; // base OS only, plugin not installed • show ad for plugin
|
||||
} else {
|
||||
// plugin is installed but if the unraid-api file doesn't fully install it's a failed install
|
||||
if (file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net')) $pluginInstalled = 'dynamix.unraid.net.plg';
|
||||
if (file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net.staging')) $pluginInstalled = 'dynamix.unraid.net.staging.plg';
|
||||
if (file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net')) $connectPluginInstalled = 'dynamix.unraid.net.plg';
|
||||
if (file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net.staging')) $connectPluginInstalled = 'dynamix.unraid.net.staging.plg';
|
||||
// plugin install failed • append failure detected so we can show warning about failed install via UPC
|
||||
if (!file_exists('/usr/local/sbin/unraid-api')) $pluginInstalled = $pluginInstalled . '_installFailed';
|
||||
if (!file_exists('/usr/local/sbin/unraid-api')) $connectPluginInstalled = $connectPluginInstalled . '_installFailed';
|
||||
}
|
||||
|
||||
$serverData = [
|
||||
"apiKey" => $myservers['upc']['apikey'] ?? '',
|
||||
"apiVersion" => $myservers['api']['version'] ?? '',
|
||||
"avatar" => (!empty($myservers['remote']['avatar']) && $pluginInstalled) ? $myservers['remote']['avatar'] : '',
|
||||
"avatar" => (!empty($myservers['remote']['avatar']) && $connectPluginInstalled) ? $myservers['remote']['avatar'] : '',
|
||||
"banner" => $display['banner'] ?? '',
|
||||
"bannerGradient" => $display['showBannerGradient'] ?? 'yes',
|
||||
"bgColor" => ($backgnd) ? '#'.$backgnd : '',
|
||||
@@ -103,11 +103,11 @@ $serverData = [
|
||||
: ( file_exists('/var/log/plugins/dynamix.unraid.net.staging.plg')
|
||||
? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.staging.plg 2>/dev/null'))
|
||||
: 'base-'.$var['version'] ),
|
||||
"pluginInstalled" => $pluginInstalled,
|
||||
"connectPluginInstalled" => $connectPluginInstalled,
|
||||
"protocol" => $_SERVER['REQUEST_SCHEME'],
|
||||
"regGen" => (int)$var['regGen'],
|
||||
"regGuid" => $var['regGUID'],
|
||||
"registered" => (!empty($myservers['remote']['username']) && $pluginInstalled),
|
||||
"registered" => (!empty($myservers['remote']['username']) && $connectPluginInstalled),
|
||||
"registeredTime" => $myservers['remote']['regWizTime'] ?? '',
|
||||
"site" => $_SERVER['REQUEST_SCHEME']."://".$_SERVER['HTTP_HOST'],
|
||||
"state" => strtoupper(empty($var['regCheck']) ? $var['regTy'] : $var['regCheck']),
|
||||
@@ -115,7 +115,7 @@ $serverData = [
|
||||
"theme" => $display['theme'],
|
||||
"ts" => time(),
|
||||
"uptime" => 1000*(time() - round(strtok(exec("cat /proc/uptime"),' '))),
|
||||
"username" => (!empty($myservers['remote']['username']) && $pluginInstalled) ? $myservers['remote']['username'] : '',
|
||||
"username" => (!empty($myservers['remote']['username']) && $connectPluginInstalled) ? $myservers['remote']['username'] : '',
|
||||
"wanFQDN" => $nginx['NGINX_WANFQDN'] ?? '',
|
||||
];
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ withDefaults(defineProps<Props>(), {
|
||||
});
|
||||
|
||||
const serverStore = useServerStore();
|
||||
const { avatar, pluginInstalled, registered, username } = storeToRefs(serverStore);
|
||||
const { avatar, connectPluginInstalled, registered, username } = storeToRefs(serverStore);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<figure class="group relative z-0 flex items-center justify-center w-36px h-36px rounded-full bg-gradient-to-r from-unraid-red to-orange">
|
||||
<img
|
||||
v-if="avatar && pluginInstalled && registered"
|
||||
v-if="avatar && connectPluginInstalled && registered"
|
||||
:src="avatar"
|
||||
:alt="username"
|
||||
class="absolute z-10 inset-0 w-36px h-36px rounded-full overflow-hidden">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { XCircleIcon } from '@heroicons/vue/24/solid';
|
||||
export interface ButtonProps {
|
||||
btnStyle?: 'fill' | 'outline' | 'underline';
|
||||
btnType?: 'button' | 'submit' | 'reset';
|
||||
click?: () => void;
|
||||
download?: boolean;
|
||||
external?: boolean;
|
||||
href?: string;
|
||||
@@ -31,7 +32,7 @@ const classes = computed(() => {
|
||||
<template>
|
||||
<component
|
||||
:is="href ? 'a' : 'button'"
|
||||
@click="$emit('click')"
|
||||
@click="click ?? $emit('click')"
|
||||
:href="href"
|
||||
:rel="external ? 'noopener noreferrer' : ''"
|
||||
:target="external ? '_blank' : ''"
|
||||
|
||||
@@ -43,7 +43,7 @@ const {
|
||||
keyType,
|
||||
} = storeToRefs(installKeyStore);
|
||||
const {
|
||||
pluginInstalled,
|
||||
connectPluginInstalled,
|
||||
registered,
|
||||
authAction,
|
||||
} = storeToRefs(serverStore);
|
||||
@@ -62,8 +62,8 @@ const {
|
||||
*/
|
||||
const isSettingsPage = ref<boolean>(document.location.pathname === '/Settings/ManagementAccess');
|
||||
|
||||
const showPromoCta = computed(() => callbackStatus.value === 'success' && !pluginInstalled.value);
|
||||
const showSignInCta = computed(() => pluginInstalled.value && !registered.value && authAction.value?.name === 'signIn' && accountActionType.value !== 'signIn');
|
||||
const showPromoCta = computed(() => callbackStatus.value === 'success' && !connectPluginInstalled.value);
|
||||
const showSignInCta = computed(() => connectPluginInstalled.value && !registered.value && authAction.value?.name === 'signIn' && accountActionType.value !== 'signIn');
|
||||
|
||||
const heading = computed(() => {
|
||||
switch (callbackStatus.value) {
|
||||
@@ -162,7 +162,7 @@ const { text, copy, copied, isSupported } = useClipboard({ source: keyUrl.value
|
||||
|
||||
<template #footer>
|
||||
<div v-if="callbackStatus === 'success'" class="flex flex-row-reverse justify-center gap-16px">
|
||||
<template v-if="pluginInstalled && accountActionType === 'signIn'">
|
||||
<template v-if="connectPluginInstalled && accountActionType === 'signIn'">
|
||||
<BrandButton
|
||||
v-if="isSettingsPage"
|
||||
@click="close"
|
||||
@@ -192,7 +192,7 @@ const { text, copy, copied, isSupported } = useClipboard({ source: keyUrl.value
|
||||
<BrandButton
|
||||
@click="close"
|
||||
btn-style="underline"
|
||||
:text="!pluginInstalled ? 'No Thanks' : 'Close'" />
|
||||
:text="!connectPluginInstalled ? 'No Thanks' : 'Close'" />
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
|
||||
@@ -7,10 +7,10 @@ import { useServerStore } from '~/store/server';
|
||||
|
||||
const dropdownStore = useDropdownStore()
|
||||
const { dropdownVisible } = storeToRefs(dropdownStore);
|
||||
const { pluginInstalled, registered, state, stateDataError, serverErrors } = storeToRefs(useServerStore());
|
||||
const { connectPluginInstalled, registered, state, stateDataError, serverErrors } = storeToRefs(useServerStore());
|
||||
|
||||
const showDefaultContent = computed(() => !showLaunchpad.value);
|
||||
const showLaunchpad = computed(() => state.value === 'ENOKEYFILE' || (pluginInstalled.value && !registered.value) && !stateDataError.value);
|
||||
const showLaunchpad = computed(() => state.value === 'ENOKEYFILE' || (connectPluginInstalled.value && !registered.value) && !stateDataError.value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -11,14 +11,14 @@ const myServersEnv = ref<string>('Staging');
|
||||
const devEnv = ref<string>('development');
|
||||
|
||||
const promoStore = usePromoStore();
|
||||
const { keyActions, pluginInstalled, registered, stateData } = storeToRefs(useServerStore());
|
||||
const { keyActions, connectPluginInstalled, registered, stateData } = storeToRefs(useServerStore());
|
||||
|
||||
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') ?? []);
|
||||
|
||||
const links = computed(():UserProfileLink[] => {
|
||||
return [
|
||||
...(registered.value && pluginInstalled.value
|
||||
...(registered.value && connectPluginInstalled.value
|
||||
? [
|
||||
{
|
||||
emphasize: true,
|
||||
@@ -45,13 +45,13 @@ const links = computed(():UserProfileLink[] => {
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(!registered.value && pluginInstalled.value
|
||||
...(!registered.value && connectPluginInstalled.value
|
||||
? [
|
||||
...(signInAction.value),
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(!pluginInstalled.value
|
||||
...(!connectPluginInstalled.value
|
||||
? [
|
||||
{
|
||||
click: () => {
|
||||
@@ -70,7 +70,7 @@ const links = computed(():UserProfileLink[] => {
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-y-8px min-w-300px max-w-350px">
|
||||
<header v-if="pluginInstalled" class="flex flex-row items-center justify-between mt-8px mx-8px">
|
||||
<header v-if="connectPluginInstalled" class="flex flex-row items-center justify-between mt-8px mx-8px">
|
||||
<h2 class="text-18px leading-none flex flex-row gap-x-8px items-center justify-between">
|
||||
<BrandLogoConnect gradient-start="currentcolor" gradient-stop="currentcolor" class="text-beta w-[120px]" />
|
||||
<UpcBeta />
|
||||
@@ -82,7 +82,7 @@ const links = computed(():UserProfileLink[] => {
|
||||
</header>
|
||||
<ul class="list-reset flex flex-col gap-y-4px p-0">
|
||||
<UpcDropdownError />
|
||||
<UpcDropdownConnectStatus v-if="!stateData.error && registered && pluginInstalled" class="mt-8px" />
|
||||
<UpcDropdownConnectStatus v-if="!stateData.error && registered && connectPluginInstalled" class="mt-8px" />
|
||||
|
||||
<li v-if="keyActions || links.length" class="m-8px">
|
||||
<UpcKeyline />
|
||||
|
||||
@@ -4,9 +4,9 @@ import { useServerStore } from '~/store/server';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
const { expireTime, pluginInstalled, registered, state, stateData } = storeToRefs(useServerStore());
|
||||
const { expireTime, connectPluginInstalled, registered, state, stateData } = storeToRefs(useServerStore());
|
||||
|
||||
const showConnectCopy = computed(() => (pluginInstalled.value && !registered.value && !stateData.value?.error));
|
||||
const showConnectCopy = computed(() => (connectPluginInstalled.value && !registered.value && !stateData.value?.error));
|
||||
|
||||
const heading = computed(() => {
|
||||
if (showConnectCopy.value) return 'Thank you for installing Connect!';
|
||||
|
||||
@@ -9,19 +9,19 @@ const dropdownStore = useDropdownStore();
|
||||
const { dropdownVisible } = storeToRefs(dropdownStore);
|
||||
const { errors } = storeToRefs(useErrorsStore());
|
||||
const {
|
||||
pluginInstalled,
|
||||
connectPluginInstalled,
|
||||
registered,
|
||||
state,
|
||||
stateData,
|
||||
username,
|
||||
} = storeToRefs(useServerStore());
|
||||
|
||||
const registeredAndPluginInstalled = computed(() => pluginInstalled.value && registered.value);
|
||||
const registeredAndconnectPluginInstalled = computed(() => connectPluginInstalled.value && registered.value);
|
||||
const showErrorIcon = computed(() => errors.value.length || stateData.value.error);
|
||||
|
||||
const text = computed((): string | undefined => {
|
||||
if ((stateData.value.error) && state.value !== 'EEXPIRED') return 'Fix Error';
|
||||
if (registeredAndPluginInstalled.value) return username.value;
|
||||
if (registeredAndconnectPluginInstalled.value) return username.value;
|
||||
return;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
const useOpenFeedback = () => {
|
||||
const open = (includeUnraidApiLogs: boolean) => {
|
||||
console.debug('[useOpenFeedback.open]', { includeUnraidApiLogs });
|
||||
try {
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-ignore – `FeedbackButton` will be included in 6.10.4+ DefaultPageLayout
|
||||
FeedbackButton();
|
||||
} catch (error) {
|
||||
console.error('[useOpenFeedback.open]', error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
open,
|
||||
};
|
||||
};
|
||||
|
||||
export default useOpenFeedback;
|
||||
@@ -15,18 +15,6 @@ export const startTrial = (payload: StartTrialPayload) => KeyServer
|
||||
.formUrl(payload)
|
||||
.post();
|
||||
|
||||
export interface KeyServerTroubleshootPayload {
|
||||
email: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
guid?: string; // if passed it'll be appended to the email subject instead of date/time
|
||||
comments?: string; // HONEYPOT FIELD. Passing a non-empty value for 'comments' will trigger the honeypot, thus not send an email but won't return any errors.
|
||||
}
|
||||
export const troubleshoot = (payload: KeyServerTroubleshootPayload) => KeyServer
|
||||
.url('/ips/troubleshoot')
|
||||
.formUrl(payload)
|
||||
.post();
|
||||
|
||||
export const validateGuid = (payload: { guid: string }) => KeyServer
|
||||
.url('/validate/guid')
|
||||
.formUrl(payload)
|
||||
|
||||
7
helpers/functions.ts
Normal file
7
helpers/functions.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @name OBJ_TO_STR
|
||||
* @param {object} obj
|
||||
* @returns {String}
|
||||
* @description Output key + value for each item in the object. Adds new line after each item.
|
||||
*/
|
||||
export const OBJ_TO_STR = obj => Object.entries(obj).reduce((str, [p, val]) => `${str}${p}: ${val}\n`, '');
|
||||
@@ -89,15 +89,6 @@ export const useAccountStore = defineStore('account', () => {
|
||||
type: 'signOut',
|
||||
}]);
|
||||
};
|
||||
const troubleshoot = () => {
|
||||
console.debug('[accountStore.accountStore.troubleshoot]');
|
||||
callbackStore.send(`${ACCOUNT}/connect`, [{
|
||||
server: {
|
||||
...serverStore.serverAccountPayload,
|
||||
},
|
||||
type: 'troubleshoot',
|
||||
}]);
|
||||
};
|
||||
/**
|
||||
* @description Update myservers.cfg for both Sign In & Sign Out
|
||||
* @note unraid-api requires apikey & token realted keys to be lowercase
|
||||
@@ -187,7 +178,6 @@ export const useAccountStore = defineStore('account', () => {
|
||||
replace,
|
||||
signIn,
|
||||
signOut,
|
||||
troubleshoot,
|
||||
updatePluginConfig,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ export const useDropdownStore = defineStore('dropdown', () => {
|
||||
dropdownShow();
|
||||
}
|
||||
// automatically open the launchpad dropdown after plugin install on first page load
|
||||
if (serverStore.pluginInstalled && !serverStore.registered && sessionStorage.getItem(`${baseStorageName}clickedInstallPlugin`)) {
|
||||
if (serverStore.connectPluginInstalled && !serverStore.registered && sessionStorage.getItem(`${baseStorageName}clickedInstallPlugin`)) {
|
||||
sessionStorage.removeItem(`${baseStorageName}clickedInstallPlugin`);
|
||||
dropdownShow();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,13 @@ import { defineStore, createPinia, setActivePinia } from 'pinia';
|
||||
// import { useServerStore } from '~/store/server';
|
||||
import type { ButtonProps } from '~/components/Brand/Button.vue';
|
||||
|
||||
import { OBJ_TO_STR } from '~/helpers/functions';
|
||||
|
||||
import type {
|
||||
Server,
|
||||
ServerStateData,
|
||||
} from '~/types/server';
|
||||
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
* @see https://github.com/vuejs/pinia/discussions/1085
|
||||
@@ -16,6 +23,7 @@ setActivePinia(createPinia());
|
||||
export type ErrorType = 'account' | 'callback' | 'installKey' | 'server' | 'serverState';
|
||||
export interface Error {
|
||||
actions?: ButtonProps[];
|
||||
debugServer?: Server;
|
||||
heading: string;
|
||||
level: 'error' | 'info' | 'warning';
|
||||
message: string;
|
||||
@@ -51,11 +59,100 @@ export const useErrorsStore = defineStore('errors', () => {
|
||||
errors.value.push(error);
|
||||
};
|
||||
|
||||
interface BugReportPayload {
|
||||
email: string;
|
||||
includeUnraidApiLogs: boolean;
|
||||
}
|
||||
|
||||
const openBugReport = async (payload: BugReportPayload) => {
|
||||
console.debug('[openBugReport]', payload);
|
||||
try {
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-ignore – `FeedbackButton` will be included in 6.10.4+ DefaultPageLayout
|
||||
await FeedbackButton();
|
||||
// once the modal is visible we need to select the radio to correctly show the bug report form
|
||||
let $modal = document.querySelector('.sweet-alert.visible');
|
||||
while (!$modal) {
|
||||
console.debug('[openBugReport] getting $modal…');
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
$modal = document.querySelector('.sweet-alert.visible');
|
||||
}
|
||||
console.debug('[openBugReport] $modal', $modal);
|
||||
// autofill errors into the bug report form
|
||||
if (errors.value.length) {
|
||||
let $textarea = $modal.querySelector('#bugreport_panel textarea');
|
||||
while (!$textarea) {
|
||||
console.debug('[openBugReport] getting $textarea…');
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
$textarea = $modal.querySelector('#bugreport_panel textarea');
|
||||
}
|
||||
console.debug('[openBugReport] $textarea', $textarea);
|
||||
const errorMessages = errors.value.map((error, index) => {
|
||||
const index1 = index + 1;
|
||||
let message = `• Error ${index1}: ${error.heading}\n`;
|
||||
message += `• Error ${index1} Message: ${error.message}\n`;
|
||||
message += `• Error ${index1} Level: ${error.level}\n`;
|
||||
message += `• Error ${index1} Type: ${error.type}\n`;
|
||||
if (error.ref) message += `• Error ${index1} Ref: ${error.ref}\n`;
|
||||
if (error.debugServer) message += `• Error ${index1} Debug Server: ${OBJ_TO_STR(error.debugServer)}\n`;
|
||||
return message;
|
||||
}).join('\n***************\n');
|
||||
$textarea.value += `\n##########################\n`;
|
||||
$textarea.value += `# Debug Details – Component Errors ${errors.value.length} #\n`;
|
||||
$textarea.value += `##########################\n`;
|
||||
$textarea.value += errorMessages;
|
||||
}
|
||||
if (payload.email) {
|
||||
// autofill emails
|
||||
let $emailInputs = $modal.querySelectorAll('[type="email"]');
|
||||
while (!$emailInputs) {
|
||||
console.debug('[openBugReport] getting $emailInputs…');
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
$emailInputs = $modal.querySelectorAll('[type="email"]');
|
||||
}
|
||||
console.debug('[openBugReport] $emailInputs', $emailInputs);
|
||||
$emailInputs.forEach($input => {
|
||||
$input.value = payload.email;
|
||||
});
|
||||
} else {
|
||||
// focus email input within bugreport_panel
|
||||
let $emailInput = $modal.querySelector('#bugreport_panel [type="email"]');
|
||||
while (!$emailInput) {
|
||||
console.debug('[openBugReport] getting $emailInput…');
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
$emailInput = $modal.querySelector('#bugreport_panel [type="email"]');
|
||||
}
|
||||
console.debug('[openBugReport] $emailInput', $emailInput);
|
||||
$emailInput.focus();
|
||||
}
|
||||
// select the radio to correctly show the bug report form
|
||||
let $myRadio: HTMLInputElement | null = $modal.querySelector('#optBugReport');
|
||||
while (!$myRadio) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
$myRadio = $modal.querySelector('#optBugReport');
|
||||
}
|
||||
$myRadio.checked = true;
|
||||
// show the correct form in the modal
|
||||
let $panels = $modal.querySelectorAll('.allpanels');
|
||||
while (!$panels) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
$panels = $modal.querySelectorAll('.allpanels');
|
||||
}
|
||||
$panels.forEach($panel => {
|
||||
if ($panel.id === 'bugreport_panel') $panel.style['display'] = 'block';
|
||||
else $panel.style['display'] = 'none';
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[openBugReport]', error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
errors,
|
||||
removeErrorByIndex,
|
||||
removeErrorByRef,
|
||||
resetErrors,
|
||||
setError,
|
||||
openBugReport,
|
||||
};
|
||||
});
|
||||
|
||||
109
store/server.ts
109
store/server.ts
@@ -1,9 +1,11 @@
|
||||
import { ServerStateData } from './../types/server';
|
||||
import { defineStore, createPinia, setActivePinia } from 'pinia';
|
||||
import {
|
||||
ArrowRightOnRectangleIcon,
|
||||
CogIcon,
|
||||
GlobeAltIcon,
|
||||
KeyIcon,
|
||||
QuestionMarkCircleIcon
|
||||
} from '@heroicons/vue/24/solid';
|
||||
|
||||
import { SETTINGS_MANAGMENT_ACCESS } from '~/helpers/urls';
|
||||
@@ -21,7 +23,7 @@ import type {
|
||||
ServerStateConfigStatus,
|
||||
ServerStateData,
|
||||
ServerStateDataAction,
|
||||
ServerPluginInstalled,
|
||||
ServerconnectPluginInstalled,
|
||||
} from '~/types/server';
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
@@ -58,7 +60,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
const license = ref<string>('');
|
||||
const locale = ref<string>('');
|
||||
const name = ref<string>('');
|
||||
const pluginInstalled = ref<ServerPluginInstalled>('');
|
||||
const connectPluginInstalled = ref<ServerconnectPluginInstalled>('');
|
||||
const registered = ref<boolean>();
|
||||
const regGen = ref<number>(0);
|
||||
const regGuid = ref<string>('');
|
||||
@@ -79,7 +81,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
return false;
|
||||
});
|
||||
|
||||
const server = computed<Server>(():Server => {
|
||||
const server = computed(():Server => {
|
||||
return {
|
||||
apiKey: apiKey.value,
|
||||
avatar: avatar.value,
|
||||
@@ -96,7 +98,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
license: license.value,
|
||||
locale: locale.value,
|
||||
name: name.value,
|
||||
pluginInstalled: pluginInstalled.value,
|
||||
connectPluginInstalled: connectPluginInstalled.value,
|
||||
registered: registered.value,
|
||||
regGen: regGen.value,
|
||||
regGuid: regGuid.value,
|
||||
@@ -134,7 +136,6 @@ export const useServerStore = defineStore('server', () => {
|
||||
state: state.value,
|
||||
site: site.value,
|
||||
};
|
||||
console.debug('[serverPurchasePayload] server', server);
|
||||
return server;
|
||||
});
|
||||
|
||||
@@ -157,6 +158,35 @@ export const useServerStore = defineStore('server', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const serverDebugPayload = computed((): Server => {
|
||||
const payload = {
|
||||
apiKey: apiKey.value,
|
||||
avatar: avatar.value,
|
||||
description: description.value,
|
||||
deviceCount: deviceCount.value,
|
||||
email: email.value,
|
||||
expireTime: expireTime.value,
|
||||
flashProduct: flashProduct.value,
|
||||
flashVendor: flashVendor.value,
|
||||
guid: guid.value,
|
||||
inIframe: inIframe.value,
|
||||
lanIp: lanIp.value,
|
||||
locale: locale.value,
|
||||
name: name.value,
|
||||
connectPluginInstalled: connectPluginInstalled.value,
|
||||
registered: registered.value,
|
||||
regGen: regGen.value,
|
||||
regGuid: regGuid.value,
|
||||
site: site.value,
|
||||
state: state.value,
|
||||
uptime: uptime.value,
|
||||
username: username.value,
|
||||
wanFQDN: wanFQDN.value,
|
||||
};
|
||||
// remove any empty values from object
|
||||
return Object.fromEntries(Object.entries(payload).filter(([_, v]) => v !== null && v !== undefined && v !== ''));
|
||||
});
|
||||
|
||||
const purchaseAction: ServerStateDataAction = {
|
||||
click: () => { purchaseStore.purchase() },
|
||||
external: true,
|
||||
@@ -230,9 +260,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'ENOKEYFILE':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction, trialStartAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
humanReadable: 'No Keyfile',
|
||||
heading: `Let's Unleash your Hardware!`,
|
||||
@@ -241,9 +271,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'TRIAL':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
humanReadable: 'Trial',
|
||||
heading: 'Thank you for choosing Unraid OS!',
|
||||
@@ -252,10 +282,10 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'EEXPIRED':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction]),
|
||||
...(trialExtensionEligible.value ? [trialExtendAction] : []),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
error: true,
|
||||
humanReadable: 'Trial Expired',
|
||||
@@ -267,9 +297,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'BASIC':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([upgradeAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
humanReadable: 'Basic',
|
||||
heading: 'Thank you for choosing Unraid OS!',
|
||||
@@ -282,9 +312,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'PLUS':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([upgradeAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
humanReadable: 'Plus',
|
||||
heading: 'Thank you for choosing Unraid OS!',
|
||||
@@ -297,8 +327,8 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'PRO':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
humanReadable: 'Pro',
|
||||
heading: 'Thank you for choosing Unraid OS!',
|
||||
@@ -310,12 +340,12 @@ export const useServerStore = defineStore('server', () => {
|
||||
if (guidReplaceable.value) messageEGUID = '<p>Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.</p>';
|
||||
else if (guidReplaceable.value === false && guidBlacklisted.value) messageEGUID = `<p>The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device or choose Purchase Key.</p><p>Your Unraid registration key is ineligible for replacement as it is blacklisted.</p>`;
|
||||
else if (guidReplaceable.value === false && !guidBlacklisted.value) messageEGUID = `<p>The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device or choose Purchase Key.</p><p>Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.</p>`;
|
||||
else messageEGUID = '<p>The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device</p><p>You may also attempt to Purchase or Replace your key.</p>'; // basically guidReplaceable.value === null
|
||||
else messageEGUID = '<p>The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device.</p><p>You may also attempt to Purchase or Replace your key.</p>'; // basically guidReplaceable.value === null
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction, replaceAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
error: true,
|
||||
humanReadable: 'Flash GUID Error',
|
||||
@@ -325,9 +355,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'EGUID1':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
error: true,
|
||||
humanReadable: 'Multiple License Keys Present',
|
||||
@@ -338,24 +368,24 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'ENOKEYFILE2':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction]),
|
||||
...(pluginInstalled.value ? [recoverAction] : []),
|
||||
...(connectPluginInstalled.value ? [recoverAction] : []),
|
||||
...(registered.value ? [signOutAction] : []),
|
||||
],
|
||||
error: true,
|
||||
humanReadable: 'Missing key file',
|
||||
heading: 'Missing key file',
|
||||
message: pluginInstalled.value
|
||||
message: connectPluginInstalled.value
|
||||
? '<p>It appears that your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.</p><p>With Unraid Connect (beta) installed you may attempt to recover your key with your Unraid.net account.</p><p>If this was an expired Trial installation, you may purchase a license key.</p>'
|
||||
: '<p>It appears that your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.</p><p>If you do not have a backup copy of your license key file you may install the Unraid Connect (beta) plugin to attempt to recover your key.</p><p>If this was an expired Trial installation, you may purchase a license key.</p>',
|
||||
};
|
||||
case 'ETRIAL':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
error: true,
|
||||
humanReadable: 'Invalid installation',
|
||||
@@ -365,9 +395,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
case 'ENOKEYFILE1':
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && pluginInstalled.value ? [signInAction] : []),
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction] : []),
|
||||
...([purchaseAction, redeemAction]),
|
||||
...(registered.value && pluginInstalled.value ? [signOutAction] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction] : []),
|
||||
],
|
||||
error: true,
|
||||
humanReadable: 'No Keyfile',
|
||||
@@ -429,10 +459,23 @@ export const useServerStore = defineStore('server', () => {
|
||||
const stateDataError = computed(() => {
|
||||
if (!stateData.value?.error) return undefined;
|
||||
return {
|
||||
actions: [
|
||||
{
|
||||
click: () => {
|
||||
errorsStore.openBugReport({
|
||||
email: email.value,
|
||||
includeUnraidApiLogs: !!connectPluginInstalled.value,
|
||||
});
|
||||
},
|
||||
icon: QuestionMarkCircleIcon,
|
||||
text: 'Contact Support',
|
||||
},
|
||||
],
|
||||
debugServer: serverDebugPayload.value,
|
||||
heading: stateData.value?.heading,
|
||||
level: 'error',
|
||||
message: stateData.value?.message,
|
||||
ref: 'stateDataError',
|
||||
ref: `stateDataError__${state.value}`,
|
||||
type: 'serverState',
|
||||
};
|
||||
});
|
||||
@@ -494,7 +537,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
});
|
||||
|
||||
const pluginInstallFailed = computed((): Error | undefined => {
|
||||
if (pluginInstalled.value && pluginInstalled.value.includes('_installFailed')) {
|
||||
if (connectPluginInstalled.value && connectPluginInstalled.value.includes('_installFailed')) {
|
||||
return {
|
||||
heading: 'Unraid Connect Install Failed',
|
||||
level: 'error',
|
||||
@@ -559,7 +602,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
if (typeof data?.license !== 'undefined') license.value = data.license;
|
||||
if (typeof data?.locale !== 'undefined') locale.value = data.locale;
|
||||
if (typeof data?.name !== 'undefined') name.value = data.name;
|
||||
if (typeof data?.pluginInstalled !== 'undefined') pluginInstalled.value = data.pluginInstalled;
|
||||
if (typeof data?.connectPluginInstalled !== 'undefined') connectPluginInstalled.value = data.connectPluginInstalled;
|
||||
if (typeof data?.registered !== 'undefined') registered.value = data.registered;
|
||||
if (typeof data?.regGen !== 'undefined') regGen.value = data.regGen;
|
||||
if (typeof data?.regGuid !== 'undefined') regGuid.value = data.regGuid;
|
||||
@@ -610,7 +653,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
locale,
|
||||
lanIp,
|
||||
name,
|
||||
pluginInstalled,
|
||||
connectPluginInstalled,
|
||||
registered,
|
||||
regGen,
|
||||
regGuid,
|
||||
|
||||
@@ -48,7 +48,7 @@ export interface Server {
|
||||
license?: string;
|
||||
locale?: string;
|
||||
name?: string;
|
||||
pluginInstalled?: ServerPluginInstalled;
|
||||
connectPluginInstalled?: ServerconnectPluginInstalled;
|
||||
registered?: boolean;
|
||||
regGen?: number;
|
||||
regGuid?: string;
|
||||
@@ -120,4 +120,4 @@ export interface ServerStateData {
|
||||
withKey?: boolean; // @todo potentially remove
|
||||
}
|
||||
|
||||
export type ServerPluginInstalled = 'dynamix.unraid.net.plg' | 'dynamix.unraid.net.staging.plg' | 'dynamix.unraid.net.plg_installFailed' | 'dynamix.unraid.net.staging.plg_installFailed' | '';
|
||||
export type ServerconnectPluginInstalled = 'dynamix.unraid.net.plg' | 'dynamix.unraid.net.staging.plg' | 'dynamix.unraid.net.plg_installFailed' | 'dynamix.unraid.net.staging.plg_installFailed' | '';
|
||||
|
||||
Reference in New Issue
Block a user