refactor(web): replace key eligibility using store

This commit is contained in:
Zack Spear
2023-09-27 17:14:42 -07:00
committed by Zack Spear
parent 7dffa1a701
commit 4d3e8bee84
3 changed files with 117 additions and 88 deletions
@@ -59,12 +59,13 @@ $serverState = [
"name" => htmlspecialchars($var['NAME']),
"osVersion" => $var['version'],
"protocol" => $_SERVER['REQUEST_SCHEME'],
"regDev" => (int)$var['regDev'] ?? 0,
"regGen" => (int)$var['regGen'],
"regGuid" => @$var['regGUID'] ?? '',
"regTo" => @htmlspecialchars($var['regTo']) ?? '',
"regTm" => $var['regTm'] ? @$var['regTm'] * 1000 : '', // JS expects milliseconds
"regTy" => @$var['regTy'] ?? '',
"regUpdExpAt" => $var['regUpdExpAt'] ? @$var['regUpdExpAt'] * 1000 : '', // JS expects milliseconds
"regExp" => $var['regExp'] ? @$var['regExp'] * 1000 : '', // JS expects milliseconds
"registered" => $registered,
"registeredTime" => $myservers['remote']['regWizTime'] ?? '',
"site" => $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'],
+8 -87
View File
@@ -1,119 +1,40 @@
<script setup lang="ts">
import {
ArrowTopRightOnSquareIcon,
CheckCircleIcon,
KeyIcon,
XCircleIcon,
ShieldExclamationIcon,
} from '@heroicons/vue/24/solid';
import { storeToRefs } from 'pinia';
import type { WretchError } from 'wretch';
import { validateGuid, type ValidateGuidPayload } from '~/composables/services/keyServer';
import { useServerStore } from '~/store/server';
import BrandLoadingWhite from '~/components/Brand/LoadingWhite.vue';
import { useReplaceCheckStore } from '~/store/replaceCheck';
const replaceCheckStore = useReplaceCheckStore();
const { status, statusOutput } = storeToRefs(replaceCheckStore);
const props = defineProps<{
t: any;
}>();
const { guid, keyfile } = storeToRefs(useServerStore());
const error = ref<{
name: string;
message: string;
stack?: string | undefined;
cause?: unknown;
} | null>(null);
const status = ref<'checking' | 'eligible' | 'error' | 'ineligible' | 'ready'>(guid.value ? 'ready' : 'error');
const statusOutput = computed(() => {
switch (status.value) {
case 'eligible':
return {
color: 'green',
icon: CheckCircleIcon,
text: props.t('Eligible'),
};
case 'ineligible':
return {
color: 'red',
icon: XCircleIcon,
text: props.t('Ineligible'),
};
case 'error':
return {
color: 'red',
icon: ShieldExclamationIcon,
text: error.value?.message || 'Unknown error',
};
default: return null;
}
});
const validationResponse = ref<ValidateGuidPayload | undefined>(sessionStorage.getItem('replaceCheck') ? JSON.parse(sessionStorage.getItem('replaceCheck') as string) : undefined);
const check = async () => {
if (!guid.value) {
status.value = 'error';
error.value = { name: 'Error', message: props.t('Flash GUID required') };
}
try {
status.value = 'checking';
error.value = null;
/**
* @todo will eventually take a keyfile and provide renewal details. If this says there's a reneal key available then we'll make a separate request to replace / swap the new key. We'll also use this to update the keyfile to the new key type for legacy users.
* endpoint will be through key server
* this should happen automatically when the web components are mounted…
* account.unraid.net will do a similar thing`
*/
const response: ValidateGuidPayload = await validateGuid({
guid: guid.value,
keyfile: keyfile.value,
}).json();
sessionStorage.setItem('replaceCheck', JSON.stringify(response));
status.value = response?.replaceable ? 'eligible' : 'ineligible';
} catch (err) {
const catchError = err as WretchError;
status.value = 'error';
error.value = catchError?.message ? catchError : { name: 'Error', message: 'Unknown error' };
console.error('[ReplaceCheck.check]', catchError);
}
};
/**
* If we already have a validation response, set the status to eligible or ineligible
*/
onBeforeMount(() => {
if (validationResponse.value) {
status.value = validationResponse.value?.replaceable ? 'eligible' : 'ineligible';
}
});
</script>
<template>
<div class="flex flex-col">
<BrandButton
v-if="status === 'checking' || status === 'ready'"
@click="check"
@click="replaceCheckStore.check"
:disabled="status !== 'ready'"
:icon="status === 'checking' ? BrandLoadingWhite : KeyIcon"
:text="t('Check Eligibility')"
class="w-full sm:max-w-300px"
/>
class="w-full sm:max-w-300px" />
<p
v-else-if="statusOutput"
class="flex flex-col sm:flex-row items-start justify-between gap-4px"
>
<UiBadge :color="statusOutput.color" :icon="statusOutput.icon" size="16px">
{{ statusOutput.text }}
{{ t(statusOutput.text) }}
</UiBadge>
<BrandButton
v-if="status === 'eligible' || status === 'ineligible'"
v-if="status !== 'checking' || status === 'ready'"
btn-style="underline"
:external="true"
:href="'https://docs.unraid.net/unraid-os/manual/changing-the-flash-device/'"
+107
View File
@@ -0,0 +1,107 @@
import {
CheckCircleIcon,
XCircleIcon,
ShieldExclamationIcon,
} from '@heroicons/vue/24/solid';
import { defineStore, createPinia, setActivePinia } from 'pinia';
import type { WretchError } from 'wretch';
import { validateGuid, type ValidateGuidPayload } from '~/composables/services/keyServer';
import { useServerStore } from '~/store/server';
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085
*/
setActivePinia(createPinia());
export const useReplaceCheckStore = defineStore('replaceCheck', () => {
const serverStore = useServerStore();
const guid = computed(() => serverStore.guid);
const keyfile = computed(() => serverStore.keyfile);
const error = ref<{
name: string;
message: string;
stack?: string | undefined;
cause?: unknown;
} | null>(null);
const status = ref<'checking' | 'eligible' | 'error' | 'ineligible' | 'ready'>(guid.value ? 'ready' : 'error');
const statusOutput = computed(() => {
// text values are translated in the component
switch (status.value) {
case 'eligible':
return {
color: 'green',
icon: CheckCircleIcon,
text: 'Eligible',
};
case 'ineligible':
return {
color: 'red',
icon: XCircleIcon,
text: 'Ineligible',
};
case 'error':
return {
color: 'red',
icon: ShieldExclamationIcon,
text: error.value?.message || 'Unknown error',
};
default: return null;
}
});
const validationResponse = ref<ValidateGuidPayload | undefined>(
sessionStorage.getItem('replaceCheck')
? JSON.parse(sessionStorage.getItem('replaceCheck') as string)
: undefined
);
const check = async () => {
if (!guid.value) {
status.value = 'error';
error.value = { name: 'Error', message: 'Flash GUID required to check replacement status' };
}
if (!keyfile.value) {
status.value = 'error';
error.value = { name: 'Error', message: 'Keyfile required to check replacement status' };
}
try {
status.value = 'checking';
error.value = null;
/**
* @todo will eventually take a keyfile and provide renewal details. If this says there's a reneal key available then we'll make a separate request to replace / swap the new key. We'll also use this to update the keyfile to the new key type for legacy users.
* endpoint will be through key server
* this should happen automatically when the web components are mounted…
* account.unraid.net will do a similar thing`
*/
const response: ValidateGuidPayload = await validateGuid({
guid: guid.value,
keyfile: keyfile.value,
}).json();
sessionStorage.setItem('replaceCheck', JSON.stringify(response));
status.value = response?.replaceable ? 'eligible' : 'ineligible';
} catch (err) {
const catchError = err as WretchError;
status.value = 'error';
error.value = catchError?.message ? catchError : { name: 'Error', message: 'Unknown error' };
console.error('[ReplaceCheck.check]', catchError);
}
};
/**
* If we already have a validation response, set the status to eligible or ineligible
*/
onBeforeMount(() => {
if (validationResponse.value) {
status.value = validationResponse.value?.replaceable ? 'eligible' : 'ineligible';
}
});
return {
status,
statusOutput,
check,
};
});