refactor(web): use osVersionBranch to determine releases endpoint

This commit is contained in:
Zack Spear
2023-10-11 18:26:34 -05:00
committed by Zack Spear
parent 1098e0f0e9
commit 2aa491e6f2
10 changed files with 90 additions and 72 deletions

View File

@@ -18,7 +18,7 @@ $connectPluginVersion = file_exists('/var/log/plugins/dynamix.unraid.net.plg')
? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.staging.plg 2>/dev/null'))
: 'base-' . $var['version']);
$myservers_flash_cfg_path='/boot/config/plugins/dynamix.my.servers/myservers.cfg';
$myservers_flash_cfg_path = '/boot/config/plugins/dynamix.my.servers/myservers.cfg';
$myservers = file_exists($myservers_flash_cfg_path) ? @parse_ini_file($myservers_flash_cfg_path,true) : [];
$configErrorEnum = [
@@ -28,7 +28,8 @@ $configErrorEnum = [
"withdrawn" => 'WITHDRAWN',
];
$registered = !empty($myservers['remote']['apikey']) && $connectPluginInstalled;
$osVersionBranch = trim(@exec('plugin category /var/log/plugins/unRAIDServer.plg') ?? 'stable');
$registered = !empty($myservers['remote']['username']) && $connectPluginInstalled;
$serverState = [
"apiKey" => $myservers['upc']['apikey'] ?? '',
@@ -62,6 +63,7 @@ $serverState = [
"model" => $var['SYS_MODEL'],
"name" => htmlspecialchars($var['NAME']),
"osVersion" => $var['version'],
"osVersionBranch" => $osVersionBranch,
"protocol" => $_SERVER['REQUEST_SCHEME'],
"regDev" => @(int)$var['regDev'] ?? 0,
"regGen" => @(int)$var['regGen'],

View File

@@ -62,9 +62,9 @@ const output = computed(() => {
<template v-if="renewStatus === 'installed'">
{{ t('Your license key was automatically renewed and installed. Reload the page to see updated details.') }}
</template>
<template v-else-if="regUpdatesExpired && ineligibleText">
<!-- <template v-else-if="regUpdatesExpired && ineligibleText">
{{ t(ineligibleText, [regTy, formattedRegExp]) }}
</template>
</template> -->
</p>
<div class="flex flex-wrap items-start justify-between gap-8px">
<BrandButton

View File

@@ -23,11 +23,11 @@ const serverStore = useServerStore();
const updateOsStore = useUpdateOsStore();
const updateOsActionsStore = useUpdateOsActionsStore();
const { guid, keyfile, osVersion } = storeToRefs(serverStore);
const { guid, keyfile, osVersion, osVersionBranch } = storeToRefs(serverStore);
const { isOsVersionStable, parsedReleaseTimestamp } = storeToRefs(updateOsStore);
const { status } = storeToRefs(updateOsActionsStore);
const includeNext = ref(isOsVersionStable.value ?? false);
const includeNext = ref(osVersionBranch.value !== 'stable');
const buttonText = computed(() => {
if (status.value === 'checking') {
@@ -42,7 +42,7 @@ const check = async () => {
await updateOsStore.checkForUpdate({
cache: true,
guid: guid.value,
includeNext: includeNext.value,
osVersionBranch: osVersionBranch.value,
keyfile: keyfile.value,
osVersion: osVersion.value,
skipCache: true,

View File

@@ -38,6 +38,7 @@ const {
keyfile,
lanIp,
osVersion,
osVersionBranch,
state,
connectPluginInstalled,
} = storeToRefs(serverStore);
@@ -112,9 +113,9 @@ onBeforeMount(() => {
updateOsStore.checkForUpdate({
cache: true,
guid: guid.value,
includeNext: !isOsVersionStable.value, // if we're already on a non-stable release, include next
keyfile: keyfile.value,
osVersion: osVersion.value,
osVersionBranch: osVersionBranch.value, // if we're already on a non-stable release, include next
});
} else {
console.warn('A valid keyfile and USB Flash boot device are required to check for key renewals, key replacement eligibiliy, and OS update availability.');

View File

@@ -207,6 +207,8 @@
"Sign Out requires the local unraid-api to be running": "Sign Out requires the local unraid-api to be running",
"Unraid OS {0} Released": "Unraid OS {0} Released",
"Unraid OS {0} Update Available": "Unraid OS {0} Update Available",
"Unraid {0} Update Available": "Unraid {0} Update Available",
"{0} Update Available": "{0} Update Available",
"Unraid OS Update Available": "Unraid OS Update Available",
"Update Unraid OS confirmation required": "Update Unraid OS confirmation required",
"Please confirm the update details below": "Please confirm the update details below",

View File

@@ -8,13 +8,7 @@
*/
import AES from 'crypto-js/aes';
import Utf8 from 'crypto-js/enc-utf8';
import { createPinia, defineStore, setActivePinia } from 'pinia';
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085
*/
setActivePinia(createPinia());
import { defineStore } from 'pinia';
export type SignIn = 'signIn';
export type SignOut = 'signOut';
@@ -26,7 +20,7 @@ export type TrialExtend = 'trialExtend';
export type TrialStart = 'trialStart';
export type Purchase = 'purchase';
export type Redeem = 'redeem';
export type Renew = 'renew'
export type Renew = 'renew';
export type Upgrade = 'upgrade';
export type UpdateOs = 'updateOs';
export type AccountActionTypes = Troubleshoot | SignIn | SignOut | OemSignOut;
@@ -45,13 +39,17 @@ export interface ServerData {
flashProduct?: string;
flashVendor?: string;
guid?: string;
includeNext?: boolean;
keyfile?: string;
locale?: string;
name?: string;
osVersion?: string;
osVersionBranch?: 'stable' | 'next' | 'preview' | 'test';
registered: boolean;
regExp?: number;
regUpdatesExpired?: boolean;
regGen?: number;
regGuid?: string;
regTy?: string;
state: string;
wanFQDN?: string;
}
@@ -87,7 +85,6 @@ export interface ExternalUpdateOsAction {
export interface ServerPayload {
type: ServerActionTypes;
server: ServerData;
includeNext?: boolean;
}
export interface ServerTroubleshoot {
@@ -137,20 +134,19 @@ export const useCallbackStoreGeneric = (
console.debug('[callback.send]');
const stringifiedData = JSON.stringify({
actions: [...payload],
sender: window.location.href,
sender: window.location.href.replace('/Tools/Update', '/Main'),
type: sendType ?? callbackActions.sendType,
});
const encryptedMessage = AES.encrypt(
stringifiedData,
callbackActions.encryptionKey,
).toString();
// build and go to url
const destinationUrl = new URL(url);
/**
* Build and go to url
* @note /Tools/Update redirects to account.unraid.net/server/update-os we need to prevent any callback sends to that url to prevent redirect loops
*/
const destinationUrl = new URL(url.replace('/Tools/Update', '/Main'));
destinationUrl.searchParams.set('data', encodeURI(encryptedMessage));
// becacuse this route redirects to account.unraid.net/server/update-os we need to change it
if (destinationUrl.pathname === '/Tools/Update') {
destinationUrl.pathname = '/Main'; // don't go to "/" because that auto redirects to /Main or /Dashboard
}
console.debug('[callback.send]', encryptedMessage, destinationUrl);
if (newTab) {
window.open(destinationUrl.toString(), '_blank');

View File

@@ -89,6 +89,7 @@ export const useServerStore = defineStore('server', () => {
const locale = ref<string>('');
const name = ref<string>('');
const osVersion = ref<string>('');
const osVersionBranch = ref<'stable' | 'next' | 'preview' | 'test'>('stable');
const registered = ref<boolean>();
const regDev = ref<number>(0);
const regGen = ref<number>(0);
@@ -152,6 +153,7 @@ export const useServerStore = defineStore('server', () => {
locale: locale.value,
name: name.value,
osVersion: osVersion.value,
osVersionBranch: osVersionBranch.value,
registered: registered.value,
regDev: regDev.value,
regGen: regGen.value,
@@ -197,6 +199,7 @@ export const useServerStore = defineStore('server', () => {
keyTypeForPurchase,
locale: locale.value,
osVersion: osVersion.value,
osVersionBranch: osVersionBranch.value,
registered: registered.value ?? false,
regExp: regExp.value,
regUpdatesExpired: regUpdatesExpired.value,
@@ -220,6 +223,7 @@ export const useServerStore = defineStore('server', () => {
lanIp: lanIp.value,
name: name.value,
osVersion: osVersion.value,
osVersionBranch: osVersionBranch.value,
registered: registered.value ?? false,
regGuid: regGuid.value,
regExp: regExp.value,
@@ -249,6 +253,7 @@ export const useServerStore = defineStore('server', () => {
locale: locale.value,
name: name.value,
osVersion: osVersion.value,
osVersionBranch: osVersionBranch.value,
registered: registered.value,
regGen: regGen.value,
regGuid: regGuid.value,
@@ -757,6 +762,7 @@ export const useServerStore = defineStore('server', () => {
if (typeof data?.locale !== 'undefined') { locale.value = data.locale; }
if (typeof data?.name !== 'undefined') { name.value = data.name; }
if (typeof data?.osVersion !== 'undefined') { osVersion.value = data.osVersion; }
if (typeof data?.osVersionBranch !== 'undefined') { osVersionBranch.value = data.osVersionBranch; }
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; }
@@ -906,6 +912,7 @@ export const useServerStore = defineStore('server', () => {
lanIp,
name,
osVersion,
osVersionBranch,
registered,
regDev,
regGen,

View File

@@ -9,6 +9,7 @@ import { computed, ref } from 'vue';
import wretch from 'wretch';
import {
ACCOUNT,
OS_RELEASES,
OS_RELEASES_NEXT,
OS_RELEASES_PREVIEW,
@@ -18,9 +19,9 @@ import {
export interface RequestReleasesPayload {
cache?: boolean; // saves response to localStorage
guid: string;
includeNext?: boolean; // if a user is on a stable release and they want to see what's available on the next branch
keyfile: string;
osVersion: SemVer | string;
osVersionBranch?: 'stable' | 'next' | 'preview' | 'test';
skipCache?: boolean; // forces a refetch from the api
}
@@ -53,6 +54,7 @@ export interface CachedReleasesResponse {
export interface UpdateOsActionStore {
osVersion: SemVer | string;
osVersionBranch: 'stable' | 'next' | 'preview' | 'test';
regExp: number;
regUpdatesExpired: boolean;
}
@@ -68,39 +70,36 @@ dayjs.extend(relativeTime);
export const RELEASES_LOCAL_STORAGE_KEY = 'unraidReleasesResponse';
export const useUpdateOsStoreGeneric = (
useUpdateOsActions?: () => UpdateOsActionStore,
currentOsVersion?: SemVer | string,
interface UpdateOsStorePayload {
useUpdateOsActions?: UpdateOsActionStore;
currentOsVersion?: SemVer | string;
currentOsVersionBranch?: 'stable' | 'next' | 'preview' | 'test';
currentRegExp?: number,
currentRegUpdatesExpired?: boolean,
currentIsLoggedIn?: boolean,
currentIsPreviewUser?: boolean,
currentIsTestUser?: boolean,
) =>
}
export const useUpdateOsStoreGeneric = (payload: UpdateOsStorePayload) =>
defineStore('updateOs', () => {
console.debug('[updateOs] payload', payload);
// Since this file is shared between account.unraid.net and the web components, we need to handle the state differently
const updateOsActions = useUpdateOsActions !== undefined ? useUpdateOsActions() : undefined;
const updateOsActions = payload.useUpdateOsActions !== undefined ? payload.useUpdateOsActions() : undefined;
console.debug('[updateOs] updateOsActions', updateOsActions);
// creating refs from the passed in values so that we can use them in the computed properties
const paramCurrentOsVersion = ref<SemVer | string>(currentOsVersion ?? '');
const paramCurrentRegExp = ref<number>(currentRegExp ?? 0);
const paramCurrentRegUpdatesExpired = ref<boolean>(currentRegUpdatesExpired ?? false);
const paramCurrentIsLoggedIn = ref<boolean>(currentIsLoggedIn ?? false);
const paramCurrentIsPreviewUser = ref<boolean>(currentIsPreviewUser ?? false);
const paramCurrentIsTestUser = ref<boolean>(currentIsTestUser ?? false);
const paramCurrentOsVersion = ref<SemVer | string>(payload.currentOsVersion ?? '');
const paramCurrentOsVersionBranch = ref<SemVer | string>(payload.currentOsVersionBranch ?? '');
const paramCurrentRegExp = ref<number>(payload.currentRegExp ?? 0);
const paramCurrentRegUpdatesExpired = ref<boolean>(payload.currentRegUpdatesExpired ?? false);
// getters when set from updateOsActions we're in the webgui web components otherwise we're in account.unraid.net
const osVersion = computed(() => updateOsActions?.osVersion ?? paramCurrentOsVersion.value ?? '');
const osVersionBranch = computed(() => updateOsActions?.osVersionBranch ?? paramCurrentOsVersionBranch.value ?? '');
const regExp = computed(() => updateOsActions?.regExp ?? paramCurrentRegExp.value ?? 0);
const regUpdatesExpired = computed(() => updateOsActions?.regUpdatesExpired ?? paramCurrentRegUpdatesExpired.value ?? false);
// state
const available = ref<string>('');
const availableWithRenewal = ref<string>('');
const releases = ref<CachedReleasesResponse | undefined>(localStorage.getItem(RELEASES_LOCAL_STORAGE_KEY) ? JSON.parse(localStorage.getItem(RELEASES_LOCAL_STORAGE_KEY) ?? '') : undefined);
// getters when set from updateOsActions we're in the webgui web components otherwise we're in account.unraid.net
const osVersion = computed(() => updateOsActions?.osVersion ?? paramCurrentOsVersion.value ?? '');
const regExp = computed(() => updateOsActions?.regExp ?? paramCurrentRegExp.value ?? 0);
const regUpdatesExpired = computed(() => updateOsActions?.regUpdatesExpired ?? paramCurrentRegUpdatesExpired.value ?? false);
const isLoggedIn = computed(() => updateOsActions?.isLoggedIn ?? paramCurrentIsLoggedIn.value ?? false);
const isPreviewUser = computed(() => updateOsActions?.isPreviewUser ?? paramCurrentIsPreviewUser.value ?? false);
const isTestUser = computed(() => updateOsActions?.isTestUser ?? paramCurrentIsTestUser.value ?? false);
// getters
const parsedReleaseTimestamp = computed(() => {
if (!releases.value?.timestamp) { return undefined; }
@@ -109,11 +108,9 @@ export const useUpdateOsStoreGeneric = (
relative: dayjs().to(dayjs(releases.value?.timestamp)),
};
});
const isOsVersionStable = computed(() => !isVersionStable(osVersion.value));
const isAvailableStable = computed(() => {
if (!available.value) return undefined;
return !isVersionStable(available.value);
});
const isOsVersionStable = computed(() => isVersionStable(osVersion.value));
const isAvailableStable = computed(() => available.value ? isVersionStable(available.value) : false);
const filteredNextReleases = computed(() => {
if (!osVersion.value) return undefined;
@@ -173,7 +170,6 @@ export const useUpdateOsStoreGeneric = (
});
// actions
const setReleasesState = (response: ReleasesResponse) => {
console.debug('[setReleasesState]');
releases.value = {
timestamp: Date.now(),
response,
@@ -181,12 +177,10 @@ export const useUpdateOsStoreGeneric = (
}
const cacheReleasesResponse = () => {
console.debug('[cacheReleasesResponse]');
localStorage.setItem(RELEASES_LOCAL_STORAGE_KEY, JSON.stringify(releases.value));
};
const purgeReleasesCache = async () => {
console.debug('[purgeReleasesCache]');
releases.value = undefined;
await localStorage.removeItem(RELEASES_LOCAL_STORAGE_KEY);
};
@@ -194,7 +188,7 @@ export const useUpdateOsStoreGeneric = (
const requestReleases = async (payload: RequestReleasesPayload): Promise<ReleasesResponse | undefined> => {
console.debug('[requestReleases]', payload);
if (!payload || !payload.guid || !payload.keyfile) {
if (!payload || !payload.osVersion || !payload.osVersionBranch || !payload.guid || !payload.keyfile) {
throw new Error('Invalid Payload for updateOs.requestReleases');
}
@@ -223,17 +217,30 @@ export const useUpdateOsStoreGeneric = (
// If here we're needing to fetch a new releases…whether it's the first time or b/c the cache was expired
try {
console.debug('[requestReleases] fetching new releases');
/**
* We need two ways of determining which branch to use:
* 1. On the server webgui use the osVersionBranch param
* 2. On account.unraid.net we can use the user's auth to determine which branch to use
*/
const isOnAccountApp = window.location.origin === ACCOUNT.origin;
const useNextBranch = (!isOnAccountApp && osVersionBranch.value === 'next') || isOnAccountApp && isLoggedIn.value;
const usePreviewBranch = (!isOnAccountApp && osVersionBranch.value === 'preview') || isOnAccountApp && isLoggedIn.value && osVersionBranch.value === 'preview'; /** @todo cognito user attributes on account app...for now use existing branch */
const useTestBranch = (!isOnAccountApp && osVersionBranch.value === 'test') || isOnAccountApp && isLoggedIn.value && osVersionBranch.value === 'test'; /** @todo cognito user attributes on account app...for now use existing branch */
let releasesUrl = OS_RELEASES.toString();
if (useNextBranch) releasesUrl = OS_RELEASES_NEXT.toString();
if (usePreviewBranch || useTestBranch) releasesUrl = OS_RELEASES_PREVIEW.toString();
// if (useTestBranch) releasesUrl = OS_RELEASES_TEST.toString();
console.debug('[requestReleases] fetching new releases from', releasesUrl);
const response: ReleasesResponse = await wretch(releasesUrl).get().json();
console.debug('[requestReleases] response', response);
/**
* @note for testing with static json a structuredClone is required otherwise Vue will not provide a fully reactive object from the original static response
* const response: ReleasesResponse = await structuredClone(testReleasesResponse);
*/
let releasesUrl = OS_RELEASES.toString();
if (isLoggedIn.value) releasesUrl = OS_RELEASES_NEXT.toString();
if (isPreviewUser.value || isTestUser.value) releasesUrl = OS_RELEASES_PREVIEW.toString();
const response: ReleasesResponse = await wretch(releasesUrl).get().json();
console.debug('[requestReleases] response', response);
// save it to local state
setReleasesState(response);
@@ -250,7 +257,7 @@ export const useUpdateOsStoreGeneric = (
const checkForUpdate = async (payload: RequestReleasesPayload) => {
console.debug('[checkForUpdate]', payload);
if (!payload || !payload.osVersion || !payload.guid || !payload.keyfile) {
if (!payload || !payload.osVersion || !payload.osVersionBranch || !payload.guid || !payload.keyfile) {
console.error('[checkForUpdate] invalid payload');
throw new Error('Invalid Payload for updateOs.checkForUpdate');
}

View File

@@ -34,6 +34,7 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
// State
const osVersion = computed(() => serverStore.osVersion);
const osVersionBranch = computed(() => serverStore.osVersionBranch);
const regExp = computed(() => serverStore.regExp);
const regUpdatesExpired = computed(() => serverStore.regUpdatesExpired);
/** used when coming back from callback, this will be the release to install */
@@ -85,9 +86,9 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
});
// Actions
const initUpdateOsCallback = (includeNextReleases: boolean = false): UserProfileLink => {
const initUpdateOsCallback = (): UserProfileLink => {
return {
click: (includeNext: boolean = includeNextReleases) => {
click: () => {
callbackStore.send(
ACCOUNT_CALLBACK.toString(),
[{
@@ -99,11 +100,11 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
serverStore.inIframe,
);
},
// emphasize: true,
external: true,
external: updateOsStore.available,
icon: ArrowPathIcon,
name: 'updateOs',
text: 'Check for OS Updates',
text: updateOsStore.available ? 'Unraid OS {0} Update Available' : 'Check for OS Updates',
textParams: [updateOsStore.available],
}
};
@@ -189,6 +190,7 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
// State
callbackUpdateRelease,
osVersion,
osVersionBranch,
regExp,
regUpdatesExpired,
rebootType,
@@ -210,4 +212,6 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
};
});
export const useUpdateOsStore = useUpdateOsStoreGeneric(useUpdateOsActionsStore as unknown as () => UpdateOsActionStore);
export const useUpdateOsStore = useUpdateOsStoreGeneric({
useUpdateOsActions: useUpdateOsActionsStore as unknown as () => UpdateOsActionStore,
});

View File

@@ -56,7 +56,6 @@ export interface Server {
flashVendor?: string;
guid?: string;
inIframe?: boolean;
includeNext?: boolean;
keyfile?: string;
lanIp?: string;
license?: string;