mirror of
https://github.com/unraid/api.git
synced 2026-01-22 00:19:56 -06:00
refactor: callback generic
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
*/
|
||||
import AES from 'crypto-js/aes';
|
||||
import Utf8 from 'crypto-js/enc-utf8';
|
||||
import { defineStore, createPinia, setActivePinia } from 'pinia';
|
||||
import { createPinia, defineStore, setActivePinia, storeToRefs, type StoreDefinition, type StoreGeneric } from 'pinia';
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
@@ -27,22 +28,15 @@ export type TrialStart = 'trialStart';
|
||||
export type Purchase = 'purchase';
|
||||
export type Redeem = 'redeem';
|
||||
export type Upgrade = 'upgrade';
|
||||
export type AccountActionTypes = Troubleshoot | SignIn | SignOut | OemSignOut;
|
||||
export type AccountKeyActionTypes = Recover | Replace | TrialExtend | TrialStart;
|
||||
export type PurchaseActionTypes = Purchase | Redeem | Upgrade;
|
||||
|
||||
export type AccountAction = SignIn | SignOut | OemSignOut | Troubleshoot;
|
||||
export type AccountKeyAction = Recover | Replace | TrialExtend | TrialStart;
|
||||
export type PurchaseAction = Purchase | Redeem | Upgrade;
|
||||
|
||||
export type ServerAction = AccountAction | AccountKeyAction | PurchaseAction;
|
||||
|
||||
export interface UserInfo {
|
||||
'custom:ips_id'?: string;
|
||||
email?: string;
|
||||
email_verifed?: 'true' | 'false';
|
||||
preferred_username?: string;
|
||||
sub?: string;
|
||||
username?: string;
|
||||
}
|
||||
export type ServerActionTypes = AccountActionTypes | AccountKeyActionTypes | PurchaseActionTypes;
|
||||
|
||||
/**
|
||||
* Represents a server, payload comes from the server to account.unraid.net
|
||||
*/
|
||||
export interface ServerData {
|
||||
description?: string;
|
||||
deviceCount?: number;
|
||||
@@ -60,85 +54,94 @@ export interface ServerData {
|
||||
wanFQDN?: string;
|
||||
}
|
||||
|
||||
export interface ServerPayload {
|
||||
server: ServerData;
|
||||
type: ServerAction;
|
||||
}
|
||||
|
||||
export interface ExternalSignIn {
|
||||
type: SignIn;
|
||||
apiKey: string;
|
||||
user: UserInfo;
|
||||
}
|
||||
|
||||
export interface ExternalSignOut {
|
||||
type: SignOut | OemSignOut;
|
||||
}
|
||||
|
||||
export interface ExternalKeyActions {
|
||||
type: PurchaseAction | AccountKeyAction;
|
||||
type: PurchaseActionTypes | AccountKeyActionTypes;
|
||||
keyUrl: string;
|
||||
}
|
||||
|
||||
export type ExternalActions =
|
||||
| ExternalSignIn
|
||||
| ExternalSignOut
|
||||
| ExternalKeyActions;
|
||||
export interface ServerPayload {
|
||||
type: ServerActionTypes;
|
||||
server: ServerData;
|
||||
}
|
||||
|
||||
export type UpcActions = ServerPayload;
|
||||
export interface ServerTroubleshoot {
|
||||
type: Troubleshoot;
|
||||
server: ServerData;
|
||||
}
|
||||
|
||||
export type ExternalActions = ExternalSignIn | ExternalSignOut | ExternalKeyActions;
|
||||
|
||||
export type UpcActions = ServerPayload | ServerTroubleshoot;
|
||||
|
||||
export type SendPayloads = ExternalActions[] | UpcActions[];
|
||||
|
||||
/**
|
||||
* Payload containing all actions that are sent from account.unraid.net to the server
|
||||
*/
|
||||
export interface ExternalPayload {
|
||||
type: 'forUpc';
|
||||
actions: ExternalActions[];
|
||||
sender: string;
|
||||
type: 'forUpc';
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload containing all actions that are sent from a server to account.unraid.net
|
||||
*/
|
||||
export interface UpcPayload {
|
||||
actions: UpcActions[];
|
||||
sender: string;
|
||||
type: 'fromUpc';
|
||||
}
|
||||
|
||||
export type SendPayloads = ExternalActions[] | UpcActions[];
|
||||
|
||||
export type QueryPayloads = ExternalPayload | UpcPayload;
|
||||
|
||||
interface CallbackActionsStore {
|
||||
redirectToCallbackType: (decryptedData: QueryPayloads) => void;
|
||||
export interface UserInfo {
|
||||
'custom:ips_id'?: string;
|
||||
email?: string;
|
||||
email_verifed?: 'true' | 'false';
|
||||
preferred_username?: string;
|
||||
sub?: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
export interface CallbackActionsStore {
|
||||
saveCallbackData: (decryptedData: QueryPayloads) => void;
|
||||
encryptionKey: string;
|
||||
sendType: 'fromUpc' | 'forUpc';
|
||||
}
|
||||
|
||||
export const useCallbackStoreGeneric = (
|
||||
useCallbackActions: () => CallbackActionsStore,
|
||||
useCallbackActions: () => CallbackActionsStore
|
||||
) =>
|
||||
defineStore('callback', () => {
|
||||
const callbackActions = useCallbackActions();
|
||||
const encryptionKey = import.meta.env.VITE_CALLBACK_KEY;
|
||||
const defaultSendType = 'fromUpc';
|
||||
|
||||
const send = (url: string, payload: SendPayloads, newTab: boolean = false, sendType?: 'fromUpc' | 'forUpc') => {
|
||||
console.debug('[callback.send]', { url, payload, sendType, newTab });
|
||||
try {
|
||||
const stringifiedData = JSON.stringify({
|
||||
actions: [
|
||||
...payload,
|
||||
],
|
||||
sender: window.location.href,
|
||||
type: sendType ?? defaultSendType,
|
||||
});
|
||||
const encryptedMessage = AES.encrypt(stringifiedData, encryptionKey).toString();
|
||||
// build and go to url
|
||||
const destinationUrl = new URL(url);
|
||||
destinationUrl.searchParams.set('data', encodeURI(encryptedMessage));
|
||||
console.debug('[callback.send]', encryptedMessage, destinationUrl);
|
||||
|
||||
if (newTab) {
|
||||
window.open(destinationUrl.toString(), '_blank');
|
||||
return;
|
||||
}
|
||||
window.location.href = destinationUrl.toString();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new Error('Unable to create callback event');
|
||||
}
|
||||
const send = (url: string, payload: SendPayloads) => {
|
||||
console.debug('[callback.send]');
|
||||
const stringifiedData = JSON.stringify({
|
||||
actions: [...payload],
|
||||
sender: window.location.href,
|
||||
type: callbackActions.sendType,
|
||||
});
|
||||
const encryptedMessage = AES.encrypt(
|
||||
stringifiedData,
|
||||
callbackActions.encryptionKey,
|
||||
).toString();
|
||||
// build and go to url
|
||||
const destinationUrl = new URL(url);
|
||||
destinationUrl.searchParams.set('data', encodeURI(encryptedMessage));
|
||||
console.debug('[callback.send]', encryptedMessage, destinationUrl);
|
||||
window.location.href = destinationUrl.toString();
|
||||
return;
|
||||
};
|
||||
|
||||
const watcher = () => {
|
||||
@@ -150,16 +153,11 @@ export const useCallbackStoreGeneric = (
|
||||
return console.debug('[callback.watcher] no callback to handle');
|
||||
}
|
||||
|
||||
try {
|
||||
const decryptedMessage = AES.decrypt(callbackValue, encryptionKey);
|
||||
const decryptedData: QueryPayloads = JSON.parse(decryptedMessage.toString(Utf8));
|
||||
console.debug('[callback.watcher]', decryptedMessage, decryptedData);
|
||||
// Parse the data and perform actions
|
||||
callbackActions.redirectToCallbackType(decryptedData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new Error('Couldn\'t decrypt callback data');
|
||||
}
|
||||
const decryptedMessage = AES.decrypt(callbackValue, callbackActions.encryptionKey);
|
||||
const decryptedData: QueryPayloads = JSON.parse(decryptedMessage.toString(Utf8));
|
||||
console.debug('[callback.watcher]', decryptedMessage, decryptedData);
|
||||
// Parse the data and perform actions
|
||||
callbackActions.saveCallbackData(decryptedData);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { addPreventClose, removePreventClose } from '~/composables/preventClose'
|
||||
import { useAccountStore } from '~/store/account';
|
||||
import { useInstallKeyStore } from '~/store/installKey';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useCallbackStoreGeneric, type ExternalPayload, type ExternalKeyActions, type QueryPayloads } from '~/store/callback';
|
||||
import { useCallbackStoreGeneric, type CallbackActionsStore, type ExternalKeyActions, type QueryPayloads } from '~/store/callback';
|
||||
|
||||
export const useCallbackActionsStore = defineStore('callbackActions', () => {
|
||||
const accountStore = useAccountStore();
|
||||
@@ -14,20 +14,34 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
|
||||
type CallbackStatus = 'closing' | 'error' | 'loading' | 'ready' | 'success';
|
||||
const callbackStatus = ref<CallbackStatus>('ready');
|
||||
|
||||
const callbackData = ref<ExternalPayload>();
|
||||
const callbackData = ref<QueryPayloads>();
|
||||
const callbackError = ref();
|
||||
|
||||
const redirectToCallbackType = (decryptedData: QueryPayloads) => {
|
||||
console.debug('[redirectToCallbackType]', { decryptedData });
|
||||
const saveCallbackData = (
|
||||
decryptedData?: QueryPayloads,
|
||||
) => {
|
||||
console.debug('[saveCallbackData]', { decryptedData });
|
||||
|
||||
if (!decryptedData.type || decryptedData.type === 'fromUpc' || !decryptedData.actions?.length) {
|
||||
if (decryptedData) {
|
||||
callbackData.value = decryptedData;
|
||||
}
|
||||
|
||||
if (!callbackData.value) {
|
||||
return console.error('Saved callback data not found');
|
||||
}
|
||||
|
||||
redirectToCallbackType?.();
|
||||
};
|
||||
|
||||
const redirectToCallbackType = () => {
|
||||
console.debug('[redirectToCallbackType]');
|
||||
|
||||
if (!callbackData.value || !callbackData.value.type || callbackData.value.type !== 'forUpc' || !callbackData.value.actions?.length) {
|
||||
callbackError.value = 'Callback redirect type not present or incorrect';
|
||||
callbackStatus.value = 'ready'; // default status
|
||||
return console.error('[redirectToCallbackType]', callbackError.value);
|
||||
}
|
||||
|
||||
// Display the feedback modal
|
||||
callbackData.value = decryptedData;
|
||||
callbackStatus.value = 'loading';
|
||||
|
||||
// Parse the data and perform actions
|
||||
@@ -71,11 +85,17 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
|
||||
});
|
||||
|
||||
return {
|
||||
redirectToCallbackType,
|
||||
// state
|
||||
callbackData,
|
||||
callbackStatus,
|
||||
// actions
|
||||
redirectToCallbackType,
|
||||
saveCallbackData,
|
||||
setCallbackStatus,
|
||||
// helpers
|
||||
sendType: 'fromUpc',
|
||||
encryptionKey: import.meta.env.VITE_CALLBACK_KEY,
|
||||
};
|
||||
});
|
||||
|
||||
export const useCallbackStore = useCallbackStoreGeneric(useCallbackActionsStore);
|
||||
export const useCallbackStore = useCallbackStoreGeneric(useCallbackActionsStore as unknown as () => CallbackActionsStore);
|
||||
|
||||
Reference in New Issue
Block a user