refactor: callbacks and progress on actions

This commit is contained in:
Zack Spear
2023-06-19 17:45:02 -05:00
parent 069924b2d2
commit 10a573fb4d
18 changed files with 349 additions and 215 deletions

View File

@@ -1,2 +1,3 @@
NUXT_PUBLIC_CALLBACK_KEY=aNotSoSecretKeyUsedToObfuscateQueryParams
NUXT_PUBLIC_UNRAID_NET=https://unraid.ddev.site
NUXT_PUBLIC_UNRAID_NET=https://unraid.ddev.site
NUXT_PUBLIC_ACCOUNT=https://localhost:8008

View File

@@ -46,8 +46,8 @@ const serverState = {
expireTime,
lanIp: '192.168.0.1',
locale: 'en',
pluginInstalled: false,
registered: false,
pluginInstalled: true,
registered: true,
site: 'http://localhost:4321',
state,
uptime,

View File

@@ -3,13 +3,11 @@ import { storeToRefs } from 'pinia';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { useCallbackStore } from '~/store/callback';
import { useCallbackActionsStore } from '~/store/callbackActions';
import { usePromoStore } from '~/store/promo';
const callbackStore = useCallbackStore();
const promoStore = usePromoStore();
const { callbackFeedbackVisible } = storeToRefs(callbackStore);
const { promoVisible } = storeToRefs(promoStore);
const { callbackFeedbackVisible } = storeToRefs(useCallbackActionsStore());
const { promoVisible } = storeToRefs(usePromoStore());
</script>
<template>

View File

@@ -2,7 +2,7 @@
import { storeToRefs } from 'pinia';
import { OnClickOutside } from '@vueuse/components'
import { useCallbackStore } from '~/store/callback';
import { useCallbackStore } from '~/store/callbackActions';
import { useDropdownStore } from '~/store/dropdown';
import { useServerStore } from '~/store/server';
import type { Server } from '~/types/server';
@@ -19,7 +19,7 @@ const dropdownStore = useDropdownStore()
const serverStore = useServerStore();
const { dropdownVisible } = storeToRefs(dropdownStore);
const { name, description, lanIp, theme } = storeToRefs(serverStore);
const { name, description, lanIp, theme, stateData } = storeToRefs(serverStore);
/**
* Close dropdown when clicking outside

View File

@@ -1,9 +1,10 @@
<script lang="ts" setup>
import { useClipboard } from '@vueuse/core'
import { storeToRefs } from 'pinia';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { useAccountStore } from '~/store/account';
import { useCallbackStore } from '~/store/callback';
import { useCallbackActionsStore } from '~/store/callbackActions';
import { useInstallKeyStore } from '~/store/installKey';
export interface Props {
@@ -15,17 +16,27 @@ withDefaults(defineProps<Props>(), {
});
const accountStore = useAccountStore();
const callbackStore = useCallbackStore();
const callbackActionsStore = useCallbackActionsStore();
const installKeyStore = useInstallKeyStore();
const { updating, updateSuccess } = storeToRefs(accountStore);
const { callbackLoading, decryptedData } = storeToRefs(callbackStore);
const { installing, success } = storeToRefs(installKeyStore);
const { callbackLoading } = storeToRefs(callbackActionsStore);
const { keyUrl, installing, success } = storeToRefs(installKeyStore);
const heading = computed(() => {
callbackLoading.value ? 'Performing actions' : 'Finished performing actions';
});
const subheading = computed(() => {
callbackLoading.value ? 'Please keep this window open' : '';
});
const close = () => {
if (callbackLoading.value) return console.debug('[close] not allowed');
callbackStore.hide();
callbackActionsStore.closeCallbackFeedback();
};
const { text, copy, copied, isSupported } = useClipboard({ source: keyUrl.value });
</script>
<template>
@@ -37,22 +48,31 @@ const close = () => {
>
<div class="text-center relative w-full flex flex-col gap-y-16px">
<header>
<h1 class="text-24px font-semibold flex flex-wrap justify-center gap-x-1">Callback Feedback</h1>
<h1 class="text-24px font-semibold">{{ heading }}</h1>
<p v-if="subheading" class="text-16px opacity-80">{{ subheading }}</p>
</header>
<BrandLoading v-if="callbackLoading" class="w-90px mx-auto" />
<pre class="text-left text-black p-8px w-full overflow-scroll bg-gray-400">{{ JSON.stringify(decryptedData, null, 2) }}</pre>
<p v-if="installing">Installing License Key</p>
<template v-if="(typeof success !== undefined)">
<p v-if="success">Installed License Key</p>
<p v-else>License Key Install Failed</p>
<template v-if="installing !== undefined">
<p v-if="installing">Installing License Key</p>
<template v-else>
<p v-if="success === true">Installed License Key</p>
<template v-else-if="success === false">
<p class="text-red italic">License Key Install Failed</p>
<button v-if="isSupported" @click="copy(keyUrl)">{{ copied ? 'Copied' : 'Copy Key URL' }}</button>
<p v-else>Copy your Key URL: {{ keyUrl }}</p>
<p>Then go to <a href="/Tools/Registration">Tools > Registration</a> to manually install it</p>
</template>
</template>
</template>
<p v-if="updating">Account Connect</p>
<template v-if="(typeof success !== undefined)">
<p v-if="success">Connect config updated with your account</p>
<p v-else>Connect config failed to update</p>
<template v-if="updating !== undefined">
<p v-if="updating">Updating Connect account config</p>
<template v-else>
<p v-if="updateSuccess === true">Connect config updated with your account</p>
<p v-else-if="updateSuccess === false" class="text-red italic">Connect config failed to update</p>
</template>
</template>
<div v-if="!callbackLoading" class="w-full max-w-xs flex flex-col gap-y-16px mx-auto">

View File

@@ -7,6 +7,7 @@ import { useDropdownStore } from '~/store/dropdown';
import { usePromoStore } from '~/store/promo';
import { useServerStore } from '~/store/server';
import type { UserProfileLink } from '~/types/userProfile';
import type { ServerStateDataAction } from '~/types/server';
const myServersEnv = ref<string>('Staging');
const devEnv = ref<string>('development');
@@ -15,6 +16,9 @@ const dropdownStore = useDropdownStore();
const promoStore = usePromoStore();
const { keyActions, pluginInstalled, registered, stateData } = storeToRefs(useServerStore());
const signInAction = computed(() => stateData.value.actions?.filter((act: { name: string; }) => act.name === 'signIn') ?? []);
const signOutAction = computed(() => stateData.value.actions?.filter((act: { name: string; }) => act.name === 'signOut') ?? []);
const links = computed(():UserProfileLink[] => {
return [
...(registered.value && pluginInstalled.value
@@ -40,25 +44,13 @@ const links = computed(():UserProfileLink[] => {
text: 'Settings',
title: 'Go to Connect plugin settings',
},
{
click: () => { console.debug('signOut') },
external: true,
icon: ArrowRightOnRectangleIcon,
text: 'Sign Out',
title: 'Sign Out to Unregister your server with Connect',
},
...(signOutAction.value),
]
: []
),
...(!registered.value && pluginInstalled.value
? [
{
click: () => { console.debug('signIn') },
external: true,
icon: UserIcon,
text: 'Sign In with Unraid.net Account',
title: 'Sign In with Unraid.net Account',
},
...(signInAction.value),
]
: []
),
@@ -77,7 +69,7 @@ const links = computed(():UserProfileLink[] => {
: []
),
];
})
});
</script>
<template>

View File

@@ -1,7 +1,7 @@
/**
* @todo setup .env
*/
const ACCOUNT = 'https://account.unraid.net';
const ACCOUNT = 'https://localhost:8008/connect';
const CONNECT_DOCS = 'https://docs.unraid.net/category/unraid-connect';
const CONNECT_DASHBOARD = 'https://connect.myunraid.net';
const DEV_GRAPH_URL = '';

View File

@@ -15,11 +15,11 @@ export default defineNuxtConfig({
{ path: '~/components/UserProfile', prefix: 'Upc' },
'~/components',
],
// runtimeConfig: {
// public: { // will be exposed to the client-side
// callbackKey: '', // set in .env https://nuxt.com/docs/guide/going-further/runtime-config#environment-variables
// }
// },
runtimeConfig: {
public: { // will be exposed to the client-side
callbackKey: 'Uyv2o8e*FiQe8VeLekTqyX6Z*8XonB', // set in .env https://nuxt.com/docs/guide/going-further/runtime-config#environment-variables
}
},
customElements: {
entries: [
{

View File

@@ -1,5 +1,5 @@
import { defineStore, createPinia, setActivePinia } from 'pinia';
import { useCallbackStore } from './callback';
import { useCallbackStore } from './callbackActions';
import { useServerStore } from './server';
import { WebguiUpdate } from '~/composables/services/webgui';
import type { CallbackAction } from '~/types/callback';
@@ -12,56 +12,62 @@ setActivePinia(createPinia());
export const useAccountStore = defineStore('account', () => {
const callbackStore = useCallbackStore();
const serverStore = useServerStore();
// State
const updating = ref(false);
const updateSuccess = ref<boolean|undefined>(undefined);
const updating = ref<boolean | undefined>(undefined);
const updateSuccess = ref<boolean | undefined>(undefined);
// Actions
const recover = () => {
console.debug('[recover]');
callbackStore.send('https://account.unraid.net', {
...serverStore.serverAccountPayload,
console.debug('[accountStore.recover]');
callbackStore.send('https://localhost:8008/connect', [{
server: {
...serverStore.serverAccountPayload,
},
type: 'recover',
});
}]);
};
const replace = () => {
console.debug('[replace]');
callbackStore.send('https://account.unraid.net', {
...serverStore.serverAccountPayload,
console.debug('[accountStore.replace]');
callbackStore.send('https://localhost:8008/connect', [{
server: {
...serverStore.serverAccountPayload,
},
type: 'replace',
});
}]);
};
const signIn = () => {
console.debug('[signIn]');
callbackStore.send('https://account.unraid.net', {
...serverStore.serverAccountPayload,
console.debug('[accountStore.signIn]');
callbackStore.send('https://localhost:8008/connect', [{
server: {
...serverStore.serverAccountPayload,
},
type: 'signIn',
});
}]);
};
const signOut = () => {
console.debug('[signOut]');
callbackStore.send('https://account.unraid.net', {
...serverStore.serverAccountPayload,
console.debug('[accountStore.accountStore.signOut]');
callbackStore.send('https://localhost:8008/connect', [{
server: {
...serverStore.serverAccountPayload,
},
type: 'signOut',
});
}]);
};
/**
* @description Update myservers.cfg for both Sign In & Sign Out
* @note unraid-api requires apikey & token realted keys to be lowercase
*/
const updatePluginConfig = async (action: CallbackAction) => {
console.debug('[updatePluginConfig]', action);
console.debug('[accountStore.updatePluginConfig]', action);
updating.value = true;
const userPayload = {
...(action.user
? {
accesstoken: action.user.signInUserSession.accessToken.jwtToken,
apikey: serverStore.apiKey,
// avatar: action.user?.attributes.avatar,
email: action.user?.attributes.email,
idtoken: action.user.signInUserSession.idToken.jwtToken,
refreshtoken: action.user.signInUserSession.refreshToken.token,
email: action.user?.email,
regWizTime: `${Date.now()}_${serverStore.guid}`, // set when signing in the first time and never unset for the sake of displaying Sign In/Up in the UPC without needing to validate guid every time
username: action.user?.attributes.preferred_username,
username: action.user?.preferred_username,
}
: {
accesstoken: '',
@@ -81,17 +87,25 @@ export const useAccountStore = defineStore('account', () => {
'#section': 'remote',
...userPayload,
})
.post();
console.debug('[updatePluginConfig] WebguiUpdate response', response);
updateSuccess.value = true;
} catch (error) {
console.debug('[updatePluginConfig] WebguiUpdate error', error);
updateSuccess.value = false;
.post()
.res(res => {
console.debug('[accountStore.updatePluginConfig] WebguiUpdate res', res);
updateSuccess.value = true;
})
.catch(err => {
console.debug('[accountStore.updatePluginConfig] WebguiUpdate err', err);
updateSuccess.value = false;
});
return response;
} finally {
updating.value = false;
}
};
watch(updating, (newV, oldV) => {
console.debug('[updating.watch]', newV, oldV);
});
return {
// State
updating,

View File

@@ -1,116 +1,153 @@
import AES from 'crypto-js/aes';
import Utf8 from 'crypto-js/enc-utf8';
import { ref } from 'vue';
import { defineStore, createPinia, setActivePinia } from 'pinia';
import { useAccountStore } from './account';
import { useInstallKeyStore } from './installKey';
import type { CallbackSendPayload, CallbackReceivePayload } from '~/types/callback';
import type {
ServerAccountCallbackSendPayload,
ServerPurchaseCallbackSendPayload,
} from '~/types/server';
export interface ServerAccountCallbackServerData {
description?: string;
deviceCount?: number;
expireTime?: number;
flashProduct?: string;
flashVendor?: string;
guid?: string;
keyfile?: string;
locale?: string;
name?: string;
registered: boolean;
regGen?: number;
regGuid?: string;
state: string;
wanFQDN?: string;
}
export type ServerStateDataActionType =
| 'signIn'
| 'signOut'
| 'purchase'
| 'redeem'
| 'upgrade'
| 'recover'
| 'replace'
| 'trialExtend'
| 'trialStart';
export interface ServerPayload {
server: ServerAccountCallbackServerData;
type: ServerStateDataActionType;
}
export interface ExternalSignIn {
type: 'signIn';
apiKey: string;
user: UserInfo;
}
export interface ExternalSignOut {
type: 'signOut';
}
export interface ExternalKeyActions {
type: 'recover' | 'replace' | 'trialExtend' | 'trialStart';
keyUrl: string;
}
export interface ExternalPurchaseActions {
type: 'purchase' | 'redeem' | 'upgrade';
}
export type ExternalActions =
| ExternalSignIn
| ExternalSignOut
| ExternalKeyActions
| ExternalPurchaseActions;
export type UpcActions = ServerPayload;
export interface ExternalPayload {
actions: ExternalActions[];
sender: string;
type: 'forUpc';
}
export interface UpcPayload {
actions: UpcActions[];
sender: string;
type: 'fromUpc';
}
export type SendPayloads = ExternalActions[] | UpcActions[];
export type QueryPayloads = ExternalPayload | UpcPayload;
export interface UserInfo {
'custom:ips_id'?: string;
email?: string;
email_verifed?: 'true' | 'false';
preferred_username?: string;
sub?: string;
username?: string;
}
interface CallbackActionsStore {
redirectToCallbackType: (decryptedData: QueryPayloads) => void;
encryptionKey: string;
sendType: 'fromUpc' | 'forUpc';
}
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085
*/
setActivePinia(createPinia());
export const useCallbackStore = defineStore('callback', () => {
// store helpers
const accountStore = useAccountStore();
const installKeyStore = useInstallKeyStore();
// const config = useRuntimeConfig(); // results in a nuxt error after web components are built
// const encryptKey = config.public.callbackKey;
const encryptKey = 'Uyv2o8e*FiQe8VeLekTqyX6Z*8XonB';
// state
const callbackFeedbackVisible = ref<boolean>(false);
const callbackLoading = ref(false);
const decryptedData = ref<CallbackReceivePayload|null>(null);
const encryptedMessage = ref<string|null>(null);
// actions
const send = (url: string = 'https://unraid.ddev.site/init-purchase', payload: CallbackSendPayload) => {
console.debug('[send]');
const stringifiedData = JSON.stringify({
...payload,
sender: window.location.href,
});
encryptedMessage.value = AES.encrypt(stringifiedData, encryptKey).toString();
// build and go to url
const destinationUrl = new URL(url);
console.debug('[send]', encryptedMessage.value, url);
destinationUrl.searchParams.set('data', encryptedMessage.value);
window.location.href = destinationUrl.toString();
};
const watcher = () => {
console.debug('[watcher]');
const currentUrl = new URL(window.location.toString());
const callbackValue = currentUrl.searchParams.get('data');
console.debug('[watcher]', { callbackValue });
if (!callbackValue) {
return console.debug('[watcher] no callback to handle');
}
callbackLoading.value = true;
const decryptedMessage = AES.decrypt(callbackValue, encryptKey);
decryptedData.value = JSON.parse(decryptedMessage.toString(Utf8));
console.debug('[watcher]', decryptedMessage, decryptedData.value);
if (!decryptedData.value) {
callbackLoading.value = false;
return console.error('Callback Watcher: Data not present');
}
if (!decryptedData.value.actions) {
callbackLoading.value = false;
return console.error('Callback Watcher: Required "action" not present');
}
// Display the feedback modal
show();
// Parse the data and perform actions
decryptedData.value.actions.forEach(async (action, index, array) => {
console.debug('[action]', action);
if (action.keyUrl) {
const response = await installKeyStore.install(action);
console.debug('[action] installKeyStore.install response', response);
}
if (action.user) {
const response = await accountStore.updatePluginConfig(action);
console.debug('[action] accountStore.updatePluginConfig', response);
}
// all actions have run
if (array.length === (index + 1)) {
console.debug('[actions] DONE');
callbackLoading.value = false;
}
});
};
const hide = () => {
console.debug('[hide]');
callbackFeedbackVisible.value = false;
};
const show = () => {
console.debug('[show]');
callbackFeedbackVisible.value = true;
}
const toggle = useToggle(callbackFeedbackVisible);
watch(callbackFeedbackVisible, (newVal, _oldVal) => {
console.debug('[callbackFeedbackVisible]', newVal);
// removing query string once actions are done so users can't refresh the page and go through the same actions
if (newVal === false) {
console.debug('[callbackFeedbackVisible] push history w/o query');
window.history.pushState(null, '', window.location.pathname);
}
});
return {
export const useCallbackStoreGeneric = (
useCallbackActions: () => CallbackActionsStore,
) =>
defineStore('callback', () => {
const callbackActions = useCallbackActions();
const encryptionKey = 'Uyv2o8e*FiQe8VeLekTqyX6Z*8XonB';
const sendType = 'fromUpc';
// state
callbackFeedbackVisible,
callbackLoading,
decryptedData,
const callbackLoading = ref(false);
const encryptedMessage = ref<string | null>(null);
// actions
send,
watcher,
hide,
show,
toggle,
};
});
const send = (url: string, payload: SendPayloads) => {
console.debug('[callback.send]');
const stringifiedData = JSON.stringify({
actions: [
...payload,
],
sender: window.location.href,
type: sendType,
});
encryptedMessage.value = AES.encrypt(stringifiedData, encryptionKey).toString();
// build and go to url
const destinationUrl = new URL(url);
destinationUrl.searchParams.set('data', encodeURI(encryptedMessage.value));
console.debug('[callback.send]', encryptedMessage.value, destinationUrl);
window.location.href = destinationUrl.toString();
return;
};
const watcher = () => {
console.debug('[callback.watcher]');
const currentUrl = new URL(window.location.toString());
const callbackValue = decodeURI(currentUrl.searchParams.get('data') ?? '');
console.debug('[callback.watcher]', { callbackValue });
if (!callbackValue) {
return console.debug('[callback.watcher] no callback to handle');
}
callbackLoading.value = true;
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);
};
return {
// state
callbackLoading,
// actions
send,
watcher,
};
});

68
store/callbackActions.ts Normal file
View File

@@ -0,0 +1,68 @@
import { defineStore } from 'pinia';
import { useAccountStore } from './account';
import { useInstallKeyStore } from './installKey';
import { useCallbackStoreGeneric, type UpcActions, type QueryPayloads } from './callback';
import { useServerStore } from './server';
export const useCallbackActionsStore = defineStore(
'callbackActions',
() => {
const accountStore = useAccountStore();
const installKeyStore = useInstallKeyStore();
const serverStore = useServerStore();
const callbackError = ref();
const callbackLoading = ref(false);
const callbackFeedbackVisible = ref<boolean>(false);
const redirectToCallbackType = (decryptedData: QueryPayloads) => {
console.debug('[redirectToCallbackType]', { decryptedData });
if (!decryptedData.type || decryptedData.type === 'fromUpc' || !decryptedData.actions?.length) {
callbackError.value = 'Callback redirect type not present or incorrect';
return console.error('[redirectToCallbackType]', callbackError.value);
}
// Display the feedback modal
callbackFeedbackVisible.value = true;
callbackLoading.value = true;
// Parse the data and perform actions
decryptedData.actions.forEach(async (action, index, array) => {
console.debug('[action]', action);
if (action?.keyUrl) {
await installKeyStore.install(action);
}
if (action?.user || action.type === 'signOut') {
await accountStore.updatePluginConfig(action);
}
// all actions have run
if (array.length === (index + 1)) {
console.debug('[actions] DONE');
setTimeout(() => {
callbackLoading.value = false;
}, 2500);
}
});
};
const closeCallbackFeedback = () => callbackFeedbackVisible.value = false;
watch(callbackLoading, (newVal, _oldVal) => {
console.debug('[callbackLoading]', newVal);
// removing query string once actions are done so users can't refresh the page and go through the same actions
if (newVal === false) {
console.debug('[callbackLoading] push history w/o query');
window.history.pushState(null, '', window.location.pathname);
}
});
return {
redirectToCallbackType,
callbackFeedbackVisible,
callbackLoading,
closeCallbackFeedback,
}
});
export const useCallbackStore = useCallbackStoreGeneric(useCallbackActionsStore);

View File

@@ -1,6 +1,6 @@
import { defineStore, createPinia, setActivePinia } from 'pinia';
// import { useAccountStore } from './account';
// import { useCallbackStore } from './callback';
// import { useCallbackStore } from './callbackActions';
// import { useInstallKeyStore } from './installKey';
// import { useServerStore } from './server';

View File

@@ -12,12 +12,15 @@ setActivePinia(createPinia());
export const useInstallKeyStore = defineStore('installKey', () => {
const serverStore = useServerStore();
const installing = ref(false);
const success = ref<boolean|undefined>();
const keyUrl = ref<string>('');
const installing = ref<boolean | undefined>();
const success = ref<boolean | undefined>();
const install = async (action: CallbackAction) => {
console.debug('[install]');
installing.value = true;
keyUrl.value = action.keyUrl ?? '';
try {
const response = await WebguiInstallKey
.query({ url: action.keyUrl })
@@ -43,8 +46,13 @@ export const useInstallKeyStore = defineStore('installKey', () => {
}
};
watch(installing, (newV, oldV) => {
console.debug('[installing.watch]', newV, oldV);
});
return {
// State
keyUrl,
installing,
success,
// Actions

View File

@@ -1,6 +1,6 @@
import { useToggle } from '@vueuse/core';
import { defineStore, createPinia, setActivePinia } from 'pinia';
import { useCallbackStore } from './callback';
import { useCallbackStore } from './callbackActions';
import { useServerStore } from './server';
/**
@@ -18,21 +18,27 @@ export const usePurchaseStore = defineStore('purchase', () => {
const redeem = () => {
console.debug('[redeem]');
callbackStore.send('https://unraid.ddev.site/init-purchase', {
...serverStore.serverPurchasePayload,
server: {
...serverStore.serverPurchasePayload,
},
type: 'redeem',
});
};
const purchase = () => {
console.debug('[purchase]');
callbackStore.send('https://unraid.ddev.site/init-purchase', {
...serverStore.serverPurchasePayload,
server: {
...serverStore.serverPurchasePayload,
},
type: 'purchase',
});
};
const upgrade = () => {
console.debug('[upgrade]');
callbackStore.send('https://unraid.ddev.site/init-purchase', {
...serverStore.serverPurchasePayload,
server: {
...serverStore.serverPurchasePayload,
},
type: 'upgrade',
});
};

View File

@@ -111,6 +111,7 @@ export const useServerStore = defineStore('server', () => {
guid: guid.value,
keyfile: keyfile.value,
name: name.value,
registered: registered.value ?? false,
state: state.value,
wanFQDN: wanFQDN.value,
}

View File

@@ -1,5 +1,5 @@
import { defineStore, createPinia, setActivePinia } from 'pinia';
import { useCallbackStore } from './callback';
import { useCallbackStore } from './callbackActions';
import { useServerStore } from './server';
/**
@@ -14,15 +14,19 @@ export const useTrialStore = defineStore('trial', () => {
const extend = () => {
console.debug('[extend]');
callbackStore.send('https://account.unraid.net', {
...serverStore.serverAccountPayload,
callbackStore.send('https://localhost:8008/connect', {
server: {
...serverStore.serverAccountPayload,
},
type: 'trialExtend',
});
};
const start = () => {
console.debug('[start]');
callbackStore.send('https://account.unraid.net', {
...serverStore.serverAccountPayload,
callbackStore.send('https://localhost:8008/connect', {
server: {
...serverStore.serverAccountPayload,
},
type: 'trialStart',
});
};

View File

@@ -16,32 +16,16 @@ export interface UserInfo {
sub?: string;
username?: string;
}
export interface AuthUser extends CognitoUser {
attributes: UserInfo;
username?: string;
preferredMFA: ChallengeName;
signInUserSession: {
accessToken: {
jwtToken: string;
};
idToken: {
jwtToken: string;
};
refreshToken: {
token: string;
};
};
}
export interface CallbackSendPayload extends ServerAccountCallbackSendPayload, ServerPurchaseCallbackSendPayload {
export interface CallbackSendPayload {
server: ServerAccountCallbackSendPayload|ServerPurchaseCallbackSendPayload;
type: ServerStateDataActionType;
}
export interface CallbackAction {
apiKey?: string;
keyUrl?: string;
type: ServerStateDataActionType;
user?: AuthUser;
user?: UserInfo;
}
export interface CallbackReceivePayload {

View File

@@ -59,6 +59,7 @@ export interface ServerAccountCallbackSendPayload {
keyfile?: string;
locale?: string;
name?: string;
registered: boolean;
regGen?: number;
regGuid?: string;
state: string;