mirror of
https://github.com/unraid/api.git
synced 2026-01-06 00:30:22 -06:00
refactor: WIP registration update expiration
This commit is contained in:
@@ -60,9 +60,11 @@ $serverState = [
|
||||
"osVersion" => $var['version'],
|
||||
"protocol" => $_SERVER['REQUEST_SCHEME'],
|
||||
"regGen" => (int)$var['regGen'],
|
||||
"regGuid" => $var['regGUID'],
|
||||
"regTo" => $var['regTo'],
|
||||
"regTm" => $var['regTm'] * 1000, // JS expects milliseconds
|
||||
"regGuid" => @$var['regGUID'] ?? '',
|
||||
"regTo" => @$var['regTo'] ?? '',
|
||||
"regTm" => $var['regTm'] ? @$var['regTm'] * 1000 : '', // JS expects milliseconds
|
||||
"regTy" => @$var['regTy'] ?? '',
|
||||
"regUpdExpAt" => $var['regUpdExpAt'] ? @$var['regUpdExpAt'] * 1000 : '', // JS expects milliseconds
|
||||
"registered" => $registered,
|
||||
"registeredTime" => $myservers['remote']['regWizTime'] ?? '',
|
||||
"site" => $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'],
|
||||
|
||||
@@ -29,12 +29,14 @@ const randomGuid = `1111-1111-${makeid(4)}-123412341234`; // this guid is regist
|
||||
// EBLACKLISTED1
|
||||
// EBLACKLISTED2
|
||||
// ENOCONN
|
||||
const state: ServerState = 'EEXPIRED';
|
||||
const state: ServerState = 'TRIAL';
|
||||
const regTy = 'Trial';
|
||||
|
||||
const uptime = Date.now() - 60 * 60 * 1000; // 1 hour ago
|
||||
const oneHourFromNow = Date.now() + 60 * 60 * 1000; // 1 hour from now
|
||||
let expireTime = 0;
|
||||
if (state === 'TRIAL') { expireTime = Date.now() + 60 * 60 * 1000; } // in 1 hour
|
||||
if (state === 'EEXPIRED') { expireTime = uptime; } // 1 hour ago
|
||||
if (state === 'TRIAL') { expireTime = oneHourFromNow; } // in 1 hour
|
||||
else if (state === 'EEXPIRED') { expireTime = uptime; } // 1 hour ago
|
||||
|
||||
export const serverState: Server = {
|
||||
apiKey: 'unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810',
|
||||
@@ -63,6 +65,9 @@ export const serverState: Server = {
|
||||
regGen: 0,
|
||||
regTm: uptime,
|
||||
regTo: 'Zack Spear',
|
||||
regTy,
|
||||
// regUpdExpAt: oneHourFromNow,
|
||||
regUpdExpAt: uptime,
|
||||
// "regGuid": "0781-5583-8355-81071A2B0211",
|
||||
site: 'http://localhost:4321',
|
||||
state,
|
||||
|
||||
@@ -51,8 +51,8 @@ const classes = computed(() => {
|
||||
:class="classes"
|
||||
@click="click ?? $emit('click')"
|
||||
>
|
||||
<component :is="icon" v-if="icon" class="flex-shrink-0 w-14px" />
|
||||
<component v-if="icon" :is="icon" class="flex-shrink-0 w-14px" />
|
||||
{{ text }}
|
||||
<component :is="iconRight" v-if="iconRight" class="flex-shrink-0 w-14px" />
|
||||
<component v-if="iconRight" :is="iconRight" class="flex-shrink-0 w-14px" />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +14,7 @@ const { keyActions } = storeToRefs(useServerStore());
|
||||
<ul v-if="keyActions" class="flex flex-col gap-y-8px">
|
||||
<li v-for="action in keyActions" :key="action.name">
|
||||
<BrandButton
|
||||
class="w-full max-w-300px"
|
||||
class="w-full sm:max-w-300px"
|
||||
:disabled="action?.disabled"
|
||||
:external="action?.external"
|
||||
:href="action?.href"
|
||||
|
||||
@@ -28,6 +28,9 @@ import { useServerStore } from '~/store/server';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
import KeyActions from '~/components/KeyActions.vue';
|
||||
import RegistrationUpgradeExpiration from '~/components/Registration/UpgradeExpiration.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export interface Props {
|
||||
@@ -45,71 +48,59 @@ const {
|
||||
guid,
|
||||
flashVendor,
|
||||
flashProduct,
|
||||
keyActions,
|
||||
keyfile,
|
||||
regGuid,
|
||||
regTm,
|
||||
regTo,
|
||||
regTy,
|
||||
regUpdExpAt,
|
||||
regUpdExpired,
|
||||
state,
|
||||
stateData,
|
||||
stateDataError,
|
||||
} = storeToRefs(serverStore);
|
||||
|
||||
const devicesAvailable = computed(() => {
|
||||
switch(regTy.value) {
|
||||
case 'Basic':
|
||||
return 6;
|
||||
case 'Plus':
|
||||
return 12;
|
||||
case 'Pro':
|
||||
case 'Trial':
|
||||
return 'unlimited';
|
||||
}
|
||||
});
|
||||
|
||||
const items = computed(() => {
|
||||
return [
|
||||
...(regTo.value
|
||||
? [
|
||||
{
|
||||
label: 'Registered to',
|
||||
text: regTo.value,
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(regTo.value
|
||||
? [
|
||||
{
|
||||
label: 'Registered on',
|
||||
text: dayjs(regTm.value).format('YYYY-MM-DD HH:mm'),
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(regTy.value ? [{ label: t('License key type'), text: regTy.value }] : []),
|
||||
...(regTo.value ? [ { label: t('Registered to'), text: regTo.value }] : []),
|
||||
...(regTo.value ? [{ label: t('Registered on'), text: dayjs(regTm.value).format('YYYY-MM-DD HH:mm')}] : []),
|
||||
/**
|
||||
* @todo factor in grandfathered users and display a different message
|
||||
*/
|
||||
...(regTo.value
|
||||
? [
|
||||
{
|
||||
label: 'Updates Expire',
|
||||
text: dayjs(regTm.value).format('YYYY-MM-DD HH:mm'),
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(state.value === 'EGUID'
|
||||
? [
|
||||
{
|
||||
label: 'Registered GUID',
|
||||
text: regGuid.value,
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
{
|
||||
label: 'Flash GUID',
|
||||
text: guid.value,
|
||||
},
|
||||
{
|
||||
label: 'Flash Vendor',
|
||||
text: flashVendor.value,
|
||||
},
|
||||
{
|
||||
label: 'Flash Product',
|
||||
text: flashProduct.value,
|
||||
},
|
||||
{
|
||||
label: 'Attached Storage Devices',
|
||||
text: deviceCount.value,
|
||||
},
|
||||
...(regUpdExpAt.value
|
||||
? [{
|
||||
error: regUpdExpired.value,
|
||||
label: t('OS Update Eligibility'),
|
||||
component: RegistrationUpgradeExpiration,
|
||||
componentProps: { t: t },
|
||||
}]
|
||||
: []),
|
||||
...(state.value === 'EGUID' ? [{ label: t('Registered GUID'), text: regGuid.value }] : [] ),
|
||||
{ label: t('Flash GUID'), text: guid.value },
|
||||
{ label: t('Flash Vendor'), text: flashVendor.value },
|
||||
{ label: t('Flash Product'), text: flashProduct.value },
|
||||
{ label: t('Attached Storage Devices'), text: t('{0} out of {1} devices', [deviceCount.value, devicesAvailable.value]) },
|
||||
...(regUpdExpAt.value
|
||||
? [{
|
||||
label: t('License key actions'),
|
||||
component: KeyActions,
|
||||
componentProps: { t: t },
|
||||
}]
|
||||
: []),
|
||||
];
|
||||
});
|
||||
</script>
|
||||
@@ -133,22 +124,21 @@ const items = computed(() => {
|
||||
v-html="stateData.message"
|
||||
class="prose text-16px leading-relaxed whitespace-normal opacity-75"></div>
|
||||
</header>
|
||||
<section>
|
||||
<dl>
|
||||
<RegistrationItem
|
||||
v-for="item in items"
|
||||
:key="item.label"
|
||||
:label="item.label"
|
||||
:text="item.text"
|
||||
:t="t" />
|
||||
<div class="p-16px sm:px-20px sm:grid sm:grid-cols-3 sm:gap-16px">
|
||||
<dt class="text-16px font-medium leading-normal"> </dt>
|
||||
<dd class="mt-2 text-16px sm:col-span-2 sm:mt-0">
|
||||
<KeyActions :t="t" />
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
<dl>
|
||||
<RegistrationItem
|
||||
v-for="item in items"
|
||||
:key="item.label"
|
||||
:component="item?.component"
|
||||
:component-props="item?.componentProps"
|
||||
:error="item.error ?? false"
|
||||
:label="item.label"
|
||||
:text="item.text"
|
||||
>
|
||||
<template v-if="item.component" #right>
|
||||
<component :is="item.component" v-bind="item.componentProps" />
|
||||
</template>
|
||||
</RegistrationItem>
|
||||
</dl>
|
||||
</div>
|
||||
</UiCardWrapper>
|
||||
</UiPageContainer>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { ShieldExclamationIcon } from '@heroicons/vue/24/solid';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '~/store/theme';
|
||||
|
||||
export interface Props {
|
||||
error?: boolean;
|
||||
label?: string;
|
||||
text?: number | string | undefined;
|
||||
t: any;
|
||||
}
|
||||
withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
error: false,
|
||||
label: '',
|
||||
text: '',
|
||||
});
|
||||
@@ -17,15 +19,28 @@ const { darkMode } = storeToRefs(useThemeStore());
|
||||
const evenBgColor = computed(() => {
|
||||
return darkMode.value ? 'even:bg-grey-darkest' : 'even:bg-black/5';
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
console.debug('[Item.onMounted]', props);
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="evenBgColor" class="text-16px p-16px sm:px-20px sm:grid sm:grid-cols-3 sm:gap-16px">
|
||||
<dt class="font-semibold">{{ t(label) }}</dt>
|
||||
<div
|
||||
:class="[
|
||||
!error && evenBgColor,
|
||||
error && 'text-white bg-unraid-red',
|
||||
]"
|
||||
class="text-16px p-16px sm:px-20px sm:grid sm:grid-cols-3 sm:gap-16px items-start"
|
||||
>
|
||||
<dt class="font-semibold flex flex-row justify-start items-center gap-x-8px">
|
||||
<ShieldExclamationIcon v-if="error" class="w-16px h-16px fill-current" />
|
||||
<span>{{ label }}</span>
|
||||
</dt>
|
||||
<dd class="mt-4px leading-normal sm:col-span-2 sm:mt-0">
|
||||
<span v-if="text" class="opacity-75">{{ text }}</span>
|
||||
<template v-else-if="$slots['text']">
|
||||
<slot name="text"></slot>
|
||||
<span v-if="text" class="opacity-75 select-all">{{ text }}</span>
|
||||
<template v-if="$slots['right']">
|
||||
<slot name="right"></slot>
|
||||
</template>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
60
web/components/Registration/UpgradeExpiration.vue
Normal file
60
web/components/Registration/UpgradeExpiration.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import useTimeHelper from '~/composables/time';
|
||||
import { useServerStore } from '~/store/server';
|
||||
|
||||
export interface Props {
|
||||
t: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { buildStringFromValues, dateDiff, formatDate } = useTimeHelper(props.t);
|
||||
|
||||
const serverStore = useServerStore();
|
||||
const { regTy, regUpdExpAt, regUpdExpired, state } = storeToRefs(serverStore);
|
||||
|
||||
const parsedTime = ref<string>('');
|
||||
const formattedTime = computed<string>(() => formatDate(regUpdExpAt.value));
|
||||
|
||||
const output = computed(() => {
|
||||
if (!regUpdExpAt.value) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
title: regUpdExpired.value
|
||||
? props.t('Expired at {0}', [formattedTime.value])
|
||||
: props.t('Expires at {0}', [formattedTime.value]),
|
||||
text: regUpdExpired.value
|
||||
? props.t('Expired {0}', [parsedTime.value])
|
||||
: props.t('Expires in {0}', [parsedTime.value]),
|
||||
};
|
||||
});
|
||||
|
||||
const runDiff = () => {
|
||||
parsedTime.value = buildStringFromValues(dateDiff((regUpdExpAt.value).toString(), false));
|
||||
};
|
||||
|
||||
let interval: string | number | NodeJS.Timeout | undefined;
|
||||
onBeforeMount(() => {
|
||||
console.debug('[UpgradeExpiration.onBeforeMount]', props);
|
||||
runDiff();
|
||||
interval = setInterval(() => {
|
||||
runDiff();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(interval);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p
|
||||
v-if="output"
|
||||
:title="output.title"
|
||||
>
|
||||
{{ output.text }}
|
||||
</p>
|
||||
</template>
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { TransitionRoot } from '@headlessui/vue';
|
||||
import {
|
||||
ArrowTopRightOnSquareIcon,
|
||||
ArrowUturnDownIcon,
|
||||
@@ -21,8 +22,6 @@ const props = defineProps<{
|
||||
version: string;
|
||||
}>();
|
||||
|
||||
const updateOsActionsStore = useUpdateOsActionsStore();
|
||||
|
||||
const visible = ref(false);
|
||||
const toggleVisible = () => {
|
||||
visible.value = !visible.value;
|
||||
@@ -40,13 +39,7 @@ const downgradeButton = ref<UserProfileLink | undefined>({
|
||||
|
||||
<template>
|
||||
<UiCardWrapper :increased-padding="true">
|
||||
<div
|
||||
class="flex flex-col sm:flex-row sm:justify-between gap-20px sm:gap-24px"
|
||||
:class="{
|
||||
'sm:items-center': !visible,
|
||||
'sm:items-start': visible,
|
||||
}"
|
||||
>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-20px sm:gap-24px">
|
||||
<div class="grid gap-y-16px">
|
||||
<h3 class="text-20px font-semibold leading-normal flex flex-row items-center gap-8px">
|
||||
<ArrowUturnDownIcon class="w-20px shrink-0" />
|
||||
@@ -61,6 +54,7 @@ const downgradeButton = ref<UserProfileLink | undefined>({
|
||||
v-if="!visible"
|
||||
@click="toggleVisible"
|
||||
:btn-style="'outline'"
|
||||
:icon="InformationCircleIcon"
|
||||
:text="t('Learn More')" />
|
||||
|
||||
<div v-else-if="downgradeButton" class="flex flex-col sm:flex-shrink-0 items-center gap-16px">
|
||||
@@ -71,7 +65,9 @@ const downgradeButton = ref<UserProfileLink | undefined>({
|
||||
:icon="LifebuoyIcon"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
:text="t('Open a bug report')" />
|
||||
<p class="opacity-75">{{ t('Original release date {0}', [releaseDate]) }}</p>
|
||||
|
||||
<p v-if="releaseDate" class="opacity-75">{{ t('Original release date {0}', [releaseDate]) }}</p>
|
||||
|
||||
<BrandButton
|
||||
@click="downgradeButton?.click"
|
||||
btn-style="outline"
|
||||
|
||||
@@ -26,10 +26,6 @@ const updateOsActionsStore = useUpdateOsActionsStore();
|
||||
const { guid, keyfile, osVersion } = storeToRefs(serverStore);
|
||||
const { available, parsedReleaseTimestamp } = storeToRefs(updateOsStore);
|
||||
const { rebootType, rebootTypeText } = storeToRefs(updateOsActionsStore);
|
||||
|
||||
watchEffect(() => {
|
||||
console.debug('[rebootType]', rebootType.value, rebootTypeText.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
ArrowPathIcon,
|
||||
ArrowTopRightOnSquareIcon,
|
||||
BellAlertIcon,
|
||||
WrenchScrewdriverIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import dayjs from 'dayjs';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -70,7 +71,8 @@ watchEffect(() => {
|
||||
<BrandButton
|
||||
v-if="ineligible"
|
||||
href="/Tools/Registration"
|
||||
:text="t('Go to Tools > Registration')"
|
||||
:icon="WrenchScrewdriverIcon"
|
||||
:text="t('Go to Tools > Registration to fix')"
|
||||
/>
|
||||
<BrandButton
|
||||
v-else-if="available && updateButton"
|
||||
|
||||
@@ -241,6 +241,24 @@
|
||||
"Go to Tools > Update": "Go to Tools > Update",
|
||||
"A valid keyfile and USB Flash boot device are required to check for updates.": "A valid keyfile and USB Flash boot device are required to check for updates.",
|
||||
"Please fix any errors and try again.": "Please fix any errors and try again.",
|
||||
"Go to Tools > Registration": "Go to Tools > Registration",
|
||||
"Original release date {0}": "Original release date {0}"
|
||||
"Go to Tools > Registration to fix": "Go to Tools > Registration to fix",
|
||||
"Original release date {0}": "Original release date {0}",
|
||||
"Registered to": "Registered to",
|
||||
"Registered on": "Registered on",
|
||||
"Updates Expire": "Updates Expire",
|
||||
"Flash GUID": "Flash GUID",
|
||||
"Flash Vendor": "Flash Vendor",
|
||||
"Flash Product": "Flash Product",
|
||||
"Attached Storage Devices": "Attached Storage Devices",
|
||||
"{0} out of {1} devices": "{0} out of {1} devices",
|
||||
"Unable to check for updates": "Unable to check for updates",
|
||||
"License key actions": "License key actions",
|
||||
"License key type": "License key type",
|
||||
"OS Update Eligibility Expiration": "OS Update Eligibility Expiration",
|
||||
"Expired at {0}": "Expired at {0}",
|
||||
"Expires at {0}": "Expires at {0}",
|
||||
"Expired {0}": "Expired {0}",
|
||||
"Expires in {0}": "Expires in {0}",
|
||||
"Renew your license key now": "Renew your license key now",
|
||||
"Renew Key to Enable OS Updates": "Renew Key to Enable OS Updates"
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
const regGuid = ref<string>('');
|
||||
const regTm = ref<number>(0);
|
||||
const regTo = ref<string>('');
|
||||
const regTy = ref<string>('');
|
||||
const regUpdExpAt = ref<number>(0);
|
||||
const regUpdExpired = computed(() => regUpdExpAt.value < Date.now()); // @todo temp solution until webgui provides
|
||||
const site = ref<string>('');
|
||||
const state = ref<ServerState>();
|
||||
const theme = ref<Theme>();
|
||||
@@ -262,6 +265,13 @@ export const useServerStore = defineStore('server', () => {
|
||||
name: 'redeem',
|
||||
text: 'Redeem Activation Code',
|
||||
};
|
||||
const renewAction = ref<ServerStateDataAction>({
|
||||
click: () => { purchaseStore.redeem(); },
|
||||
external: true,
|
||||
icon: KeyIcon,
|
||||
name: 'renew',
|
||||
text: 'Renew Key to Enable OS Updates',
|
||||
});
|
||||
const replaceAction: ServerStateDataAction = {
|
||||
click: () => { accountStore.replace(); },
|
||||
external: true,
|
||||
@@ -716,6 +726,8 @@ export const useServerStore = defineStore('server', () => {
|
||||
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; }
|
||||
if (typeof data?.regTy !== 'undefined') { regTy.value = data.regTy; }
|
||||
if (typeof data?.regUpdExpAt !== 'undefined') { regUpdExpAt.value = data.regUpdExpAt; }
|
||||
if (typeof data?.site !== 'undefined') { site.value = data.site; }
|
||||
if (typeof data?.state !== 'undefined') { state.value = data.state; }
|
||||
if (typeof data?.theme !== 'undefined') { theme.value = data.theme; }
|
||||
@@ -853,6 +865,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
regGuid,
|
||||
regTm,
|
||||
regTo,
|
||||
regTy,
|
||||
regUpdExpAt,
|
||||
regUpdExpired,
|
||||
site,
|
||||
state,
|
||||
theme,
|
||||
@@ -860,6 +875,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
username,
|
||||
refreshServerStateStatus,
|
||||
isOsVersionStable,
|
||||
renewAction,
|
||||
// getters
|
||||
authAction,
|
||||
deprecatedUnraidSSL,
|
||||
|
||||
@@ -64,6 +64,9 @@ export interface Server {
|
||||
regGuid?: string;
|
||||
regTm?: number;
|
||||
regTo?: string;
|
||||
regTy?: string;
|
||||
regUpdExpAt?: number;
|
||||
regUpdExpired?: boolean;
|
||||
site?: string;
|
||||
state?: ServerState;
|
||||
theme?: Theme | undefined;
|
||||
@@ -113,7 +116,7 @@ export interface ServerPurchaseCallbackSendPayload {
|
||||
site: string;
|
||||
}
|
||||
|
||||
export type ServerStateDataKeyActions = 'purchase' | 'redeem' | 'upgrade' | 'recover' | 'replace' | 'trialExtend' | 'trialStart' | 'updateOs';
|
||||
export type ServerStateDataKeyActions = 'purchase' | 'redeem' | 'upgrade' | 'recover' | 'renew' | 'replace' | 'trialExtend' | 'trialStart' | 'updateOs';
|
||||
|
||||
export type ServerStateDataAccountActions = 'signIn' | 'signOut' | 'troubleshoot';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user