refactor: callback generic

This commit is contained in:
Zack Spear
2023-08-28 14:01:38 -07:00
parent 93a3fb0e6b
commit f4a5c1f97b
2 changed files with 97 additions and 79 deletions

View File

@@ -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 {

View File

@@ -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);