mirror of
https://github.com/unraid/api.git
synced 2026-01-02 14:40:01 -06:00
refactor: WIP updateOs store – response caching and update version checking
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import testOsReleasesResponse from '~/_data/osReleases'; // test data
|
||||
|
||||
import { BellAlertIcon } from '@heroicons/vue/24/solid';
|
||||
import { defineStore, createPinia, setActivePinia } from 'pinia';
|
||||
import gt from 'semver/functions/gt';
|
||||
import coerce from 'semver/functions/coerce';
|
||||
import type { SemVer } from 'semver';
|
||||
|
||||
import useInstallPlugin from '~/composables/installPlugin';
|
||||
import { request } from '~/composables/services/request';
|
||||
@@ -9,7 +13,7 @@ import { useCallbackStore } from '~/store/callbackActions';
|
||||
import { useErrorsStore } from '~/store/errors';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import type { InstallPluginPayload } from '~/composables/installPlugin';
|
||||
import type { OsRelease } from '~/store/callback';
|
||||
import type { OsRelease, OsReleasesResponse } from '~/store/callback';
|
||||
import type { ServerStateDataAction } from '~/types/server';
|
||||
|
||||
/**
|
||||
@@ -18,6 +22,11 @@ import type { ServerStateDataAction } from '~/types/server';
|
||||
*/
|
||||
setActivePinia(createPinia());
|
||||
|
||||
export interface CachedOsReleasesResponse {
|
||||
timestamp: number;
|
||||
response: OsReleasesResponse;
|
||||
}
|
||||
|
||||
export const useUpdateOsStore = defineStore('updateOs', () => {
|
||||
const callbackStore = useCallbackStore();
|
||||
const errorsStore = useErrorsStore();
|
||||
@@ -27,33 +36,94 @@ export const useUpdateOsStore = defineStore('updateOs', () => {
|
||||
|
||||
// State
|
||||
const status = ref<'confirming' | 'failed' | 'ready' | 'success' | 'updating' | 'downgrading'>('ready');
|
||||
const releasesJson = ref<CachedOsReleasesResponse | undefined>(localStorage.getItem('releasesJson') ? JSON.parse(localStorage.getItem('releasesJson') ?? '') : undefined);
|
||||
const callbackUpdateRelease = ref<OsRelease | undefined>(); // used when coming back from callback, this will be the release to install
|
||||
const updateAvailable = ref<OsRelease | undefined>(); // used locally to show update action button
|
||||
const downgradeAvailable = ref<boolean>(false);
|
||||
|
||||
// Getters
|
||||
const currentOsVersion = computed((): string => serverStore?.osVersion);
|
||||
const currentOsVersion = computed(() => serverStore?.osVersion);
|
||||
const isOsVersionStable = computed(() => serverStore?.isOsVersionStable); // used to determine if we should look for stable or next releases
|
||||
// const currentOsVersionNext = computed((): boolean => serverStore?.osVersionNext);
|
||||
|
||||
// Actions
|
||||
const checkForOsUpdate = async () => {
|
||||
console.debug('[checkForOsUpdate]');
|
||||
|
||||
const fetchOsReleases = async () => {
|
||||
try {
|
||||
// const response: OsReleasesResponse = await request.url(OS_RELEASES.toString()).get().json();
|
||||
const response = testOsReleasesResponse;
|
||||
releasesJson.value = {
|
||||
timestamp: Date.now(),
|
||||
response,
|
||||
};
|
||||
localStorage.setItem('releasesJson', JSON.stringify(releasesJson.value));
|
||||
} catch (error) {
|
||||
console.error('[fetchOsReleases]', error);
|
||||
}
|
||||
};
|
||||
const purgeReleasesJsonCache = () => {
|
||||
releasesJson.value = undefined;
|
||||
localStorage.removeItem('releasesJson');
|
||||
};
|
||||
const checkForOsUpdate = async (skipCache: boolean = false, includeNext: boolean = false) => {
|
||||
if (!currentOsVersion.value) {
|
||||
return console.error('[checkForOsUpdate] currentOsVersion not found, skipping OS update check');
|
||||
}
|
||||
|
||||
const response: OsRelease[] = await request.url(OS_RELEASES.toString()).get().json();
|
||||
console.debug('[checkForOsUpdate] response', response);
|
||||
if (skipCache) { // forces new check
|
||||
purgeReleasesJsonCache();
|
||||
}
|
||||
|
||||
if (response) {
|
||||
response.forEach(release => {
|
||||
const releaseVersion = release.name.replace('Unraid ', '');
|
||||
console.debug('[checkForOsUpdate] releaseVersion', releaseVersion);
|
||||
if (gt(releaseVersion, '6.12.3')) { // currentOsVersion.value
|
||||
updateAvailable.value = release;
|
||||
return; // stop looping, we found an update
|
||||
try {
|
||||
/**
|
||||
* Compare the timestamp of the cached data to the current time,
|
||||
* if it's older than 7 days, reset releasesJson.
|
||||
* Which will trigger a new API call to get the releases.
|
||||
* Otherwise skip the API call and use the cached data.
|
||||
*/
|
||||
if (releasesJson.value) {
|
||||
const currentTime = new Date().getTime();
|
||||
const localState = releasesJson.value;
|
||||
const cacheDuration = import.meta.env.DEV ? 30000 : 604800000; // 30 seconds for testing, 7 days for prod
|
||||
if (currentTime - localState.timestamp > cacheDuration) {
|
||||
purgeReleasesJsonCache();
|
||||
await fetchOsReleases();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await fetchOsReleases();
|
||||
}
|
||||
|
||||
if (releasesJson.value && releasesJson.value.response) {
|
||||
/**
|
||||
* If we're on stable and the user hasn't requested to include next releases in the check
|
||||
* then remove next releases from the cached data
|
||||
*/
|
||||
if (!includeNext && isOsVersionStable.value && releasesJson.value.response.next) {
|
||||
delete releasesJson.value.response.next;
|
||||
}
|
||||
|
||||
Object.keys(releasesJson.value.response ?? {}).forEach(key => {
|
||||
if (!releasesJson.value) { // this is just to make TS happy (it's already checked above…thanks github copilot for knowing what I needed)
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateAvailable.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const releases = releasesJson.value.response[key as keyof OsReleasesResponse];
|
||||
|
||||
if (releases && releases.length > 0) {
|
||||
releases.find(release => {
|
||||
if (gt(release.version, currentOsVersion.value)) { /** @todo '6.12.0' temporary for dev. Replace with currentOsVersion.value */
|
||||
updateAvailable.value = release;
|
||||
return true; // stop looping, we found an update
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[checkForOsUpdate]', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,15 +148,32 @@ export const useUpdateOsStore = defineStore('updateOs', () => {
|
||||
text: 'Unraid OS Update Available',
|
||||
}
|
||||
});
|
||||
/**
|
||||
* @description When receiving the callback the Account update page we'll use the provided releaseMd5 to find the release in the releasesJson cache.
|
||||
*/
|
||||
const confirmUpdateOs = async (releaseMd5: string) => {
|
||||
/** this should never happen, but if it does we should probably try to fetch the releases again */
|
||||
if (!releasesJson.value) {
|
||||
await fetchOsReleases();
|
||||
};
|
||||
|
||||
Object.keys(releasesJson.value?.response ?? {}).forEach(key => {
|
||||
const releases = releasesJson.value?.response[key as keyof OsReleasesResponse];
|
||||
|
||||
if (releases && releases.length > 0) {
|
||||
releases.forEach(release => {
|
||||
if (release.md5 === releaseMd5) {
|
||||
callbackUpdateRelease.value = release;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
const confirmUpdateOs = (payload: OsRelease) => {
|
||||
console.debug('[confirmUpdateOs]');
|
||||
callbackUpdateRelease.value = payload;
|
||||
setStatus('confirming');
|
||||
};
|
||||
|
||||
const installOsUpdate = () => {
|
||||
console.debug('[installOsUpdate]', callbackUpdateRelease.value);
|
||||
if (!callbackUpdateRelease.value) {
|
||||
return console.error('[installOsUpdate] release not found');
|
||||
}
|
||||
@@ -100,7 +187,6 @@ export const useUpdateOsStore = defineStore('updateOs', () => {
|
||||
};
|
||||
|
||||
const downgradeOs = async () => {
|
||||
console.debug('[downgradeOs]');
|
||||
setStatus('downgrading');
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user