feat: Agent registration UX flow updates (#4942)
Co-authored-by: curiouscorrelation <curiouscorrelation@gmail.com> Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
parent
3c535b2ad4
commit
f564b2e34f
7 changed files with 170 additions and 107 deletions
|
|
@ -181,6 +181,20 @@ pub fn run() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let app_handle_ref = app_handle.clone();
|
||||||
|
app_handle.listen("maximize-window", move |_| {
|
||||||
|
tracing::info!("Maximize window event triggered");
|
||||||
|
if let Some(window) = app_handle_ref.get_webview_window("main") {
|
||||||
|
if let Err(e) = window.emit("show-otp-view", ()) {
|
||||||
|
tracing::error!("Failed to emit show-otp-view event: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = show_main_window(&app_handle_ref) {
|
||||||
|
tracing::error!("Failed to maximize window: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let app_handle_ref = app_handle.clone();
|
let app_handle_ref = app_handle.clone();
|
||||||
app_handle.listen("registration-received", move |_| {
|
app_handle.listen("registration-received", move |_| {
|
||||||
tracing::info!("Registration received event triggered");
|
tracing::info!("Registration received event triggered");
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,13 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
|
||||||
true,
|
true,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)?;
|
)?;
|
||||||
|
let maximize_window = MenuItem::with_id(
|
||||||
|
app,
|
||||||
|
"maximize_window",
|
||||||
|
"Maximize Window",
|
||||||
|
true,
|
||||||
|
None::<&str>,
|
||||||
|
)?;
|
||||||
let show_registrations = MenuItem::with_id(
|
let show_registrations = MenuItem::with_id(
|
||||||
app,
|
app,
|
||||||
"show_registrations",
|
"show_registrations",
|
||||||
|
|
@ -48,9 +55,12 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
|
||||||
.item(&app_name_item)
|
.item(&app_name_item)
|
||||||
.item(&app_version_item)
|
.item(&app_version_item)
|
||||||
.separator()
|
.separator()
|
||||||
|
.item(&maximize_window)
|
||||||
|
.separator()
|
||||||
.item(&clear_registrations)
|
.item(&clear_registrations)
|
||||||
.item(&show_registrations)
|
.item(&show_registrations)
|
||||||
.separator()
|
.separator()
|
||||||
|
.separator()
|
||||||
.item(&quit_i)
|
.item(&quit_i)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
|
@ -63,7 +73,7 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
|
||||||
})
|
})
|
||||||
.icon_as_template(cfg!(target_os = "macos"))
|
.icon_as_template(cfg!(target_os = "macos"))
|
||||||
.menu(&menu)
|
.menu(&menu)
|
||||||
.menu_on_left_click(true)
|
.show_menu_on_left_click(true)
|
||||||
.on_menu_event(move |app, event| match event.id.as_ref() {
|
.on_menu_event(move |app, event| match event.id.as_ref() {
|
||||||
"quit" => {
|
"quit" => {
|
||||||
tracing::info!("Exiting the agent...");
|
tracing::info!("Exiting the agent...");
|
||||||
|
|
@ -85,6 +95,15 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
|
||||||
tracing::error!("Failed to show window: {}", e);
|
tracing::error!("Failed to show window: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"maximize_window" => {
|
||||||
|
app.emit("maximize-window", ())
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
tracing::error!("Failed to emit maximize-window event: {}", e);
|
||||||
|
});
|
||||||
|
if let Err(e) = show_main_window(&app) {
|
||||||
|
tracing::error!("Failed to maximize window: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
tracing::warn!("Unhandled menu event: {:?}", event.id);
|
tracing::warn!("Unhandled menu event: {:?}", event.id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,18 @@
|
||||||
<div class="h-screen p-5 flex flex-col gap-y-2">
|
<div class="h-screen p-5 flex flex-col gap-y-2">
|
||||||
<h1 class="font-bold text-lg text-white">{{ pipe(state(), getTitle) }}</h1>
|
<h1 class="font-bold text-lg text-white">{{ pipe(state(), getTitle) }}</h1>
|
||||||
|
|
||||||
<template v-if="isOtpView(state())">
|
<template v-if="O.isSome(state().otp)">
|
||||||
<div v-if="state().otp" class="flex-grow">
|
<div class="flex-grow">
|
||||||
<p class="tracking-wide">
|
<p class="tracking-wide">
|
||||||
An app is trying to register against the Hoppscotch Agent. If this was intentional, copy the given code into
|
An app is trying to register against the Hoppscotch Agent. If this was intentional, copy the given code into
|
||||||
the app to complete the registration process. Please hide the window if you did not initiate this request.
|
the app to complete the registration process. Please cancel the registration if you did not initiate this request.
|
||||||
Do not hide this window until the verification code is entered. The window will hide automatically once done.
|
The window will hide automatically once registration succeeds. If you minimize this window during registration,
|
||||||
|
you can access it again from the tray by selecting "Maximize Window".
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
class="font-bold text-5xl tracking-wider text-center pt-10 text-white"
|
class="font-bold text-5xl tracking-wider text-center pt-10 text-white"
|
||||||
>{{ pipe(state().otp, O.getOrElse(() => "")) }}</p>
|
>{{ pipe(state().otp, O.getOrElse(() => "")) }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="text-center pt-10 flex-grow">
|
|
||||||
<p class="tracking-wide">Waiting for registration requests...</p>
|
|
||||||
<p
|
|
||||||
class="text-sm text-gray-400 mt-2"
|
|
||||||
>You can hide this window and access it again from the tray icon.</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
@ -38,7 +33,27 @@
|
||||||
:icon="copyIcon"
|
:icon="copyIcon"
|
||||||
@click="copyOtp"
|
@click="copyOtp"
|
||||||
/>
|
/>
|
||||||
<HoppButtonPrimary label="Hide Window" outline @click="hideWindow" />
|
<div class="flex gap-2">
|
||||||
|
<template v-if="O.isSome(state().otp)">
|
||||||
|
<HoppButtonSecondary
|
||||||
|
label="Cancel Registration"
|
||||||
|
outline
|
||||||
|
@click="getCurrentWindow().close()"
|
||||||
|
/>
|
||||||
|
<HoppButtonPrimary
|
||||||
|
label="Minimize to Tray"
|
||||||
|
outline
|
||||||
|
@click="hideWindow"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<HoppButtonPrimary
|
||||||
|
v-else
|
||||||
|
label="Minimize to Tray"
|
||||||
|
outline
|
||||||
|
@click="hideWindow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -94,10 +109,9 @@ const appState = ref<AppState>({
|
||||||
|
|
||||||
const state = () => appState.value
|
const state = () => appState.value
|
||||||
|
|
||||||
const isOtpView = (s: AppState): boolean => s.view === "otp"
|
|
||||||
const getTitle = (s: AppState): string =>
|
const getTitle = (s: AppState): string =>
|
||||||
s.view === "otp" ? "Agent Registration Request" : "Agent Registrations"
|
O.isSome(s.otp) ? "Agent Registration Request" : "Agent Registrations"
|
||||||
const shouldShowCopy = (s: AppState): boolean => isOtpView(s) && O.isSome(s.otp)
|
const shouldShowCopy = (s: AppState): boolean => O.isSome(s.otp)
|
||||||
const formatDate = (date: string): string => new Date(date).toLocaleString()
|
const formatDate = (date: string): string => new Date(date).toLocaleString()
|
||||||
|
|
||||||
const getOtp = TE.tryCatch(
|
const getOtp = TE.tryCatch(
|
||||||
|
|
@ -166,7 +180,11 @@ onMounted(async () => {
|
||||||
await pipe(
|
await pipe(
|
||||||
getOtp,
|
getOtp,
|
||||||
TE.map((otp: string) => {
|
TE.map((otp: string) => {
|
||||||
if (otp) appState.value = { ...state(), otp: O.some(otp) }
|
if (otp) {
|
||||||
|
appState.value = { ...state(), otp: O.some(otp) }
|
||||||
|
} else {
|
||||||
|
updateRegistrations();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)()
|
)()
|
||||||
|
|
||||||
|
|
@ -181,6 +199,18 @@ onMounted(async () => {
|
||||||
),
|
),
|
||||||
listen("authenticated", handleAuthenticated),
|
listen("authenticated", handleAuthenticated),
|
||||||
listen("show-registrations", handleShowRegistrations),
|
listen("show-registrations", handleShowRegistrations),
|
||||||
|
listen("show-otp-view", async () => {
|
||||||
|
await pipe(
|
||||||
|
getOtp,
|
||||||
|
TE.map((otp: string) => {
|
||||||
|
if (otp) {
|
||||||
|
appState.value = { ...state(), otp: O.some(otp) };
|
||||||
|
} else {
|
||||||
|
updateRegistrations();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)();
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,13 @@
|
||||||
:icon="copyIcon"
|
:icon="copyIcon"
|
||||||
@click="copyCode"
|
@click="copyCode"
|
||||||
/>
|
/>
|
||||||
<HoppButtonPrimary label="Hide Window" outline @click="hideWindow" />
|
<div class="flex gap-2">
|
||||||
|
<HoppButtonSecondary
|
||||||
|
label="Cancel Registration"
|
||||||
|
outline
|
||||||
|
@click="getCurrentWindow().close()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -882,14 +882,15 @@
|
||||||
"account_email_description": "Your primary email address.",
|
"account_email_description": "Your primary email address.",
|
||||||
"account_name_description": "This is your display name.",
|
"account_name_description": "This is your display name.",
|
||||||
"additional": "Additional Settings",
|
"additional": "Additional Settings",
|
||||||
"agent_not_running": "Hoppscotch Agent not detected - click `Retry` to check again.",
|
"agent_not_running": "Hoppscotch Agent not detected. Please check if the Agent is running.",
|
||||||
"agent_not_running_short": "Check Agent's status.",
|
"agent_not_running_short": "Check Agent's status.",
|
||||||
"agent_running": "Hoppscotch Agent is live.",
|
"agent_running": "Hoppscotch Agent is live.",
|
||||||
"agent_running_short": "Hoppscotch Agent is live.",
|
"agent_running_short": "Hoppscotch Agent is live.",
|
||||||
"agent_reset_registration": "Reset Registration",
|
"agent_discard_registration": "Discard Agent Registration",
|
||||||
"agent_registered": "Agent Registered",
|
"agent_registered": "Agent Registered",
|
||||||
"agent_registration_successful": "Agent Registered Successfully",
|
"agent_registration_successful": "Agent Registered Successfully",
|
||||||
"agent_registration_fetch_failed": "Couldn't fetch Agent registration information",
|
"agent_registration_fetch_failed": "Couldn't fetch Agent registration information. Please re-register the Agent.",
|
||||||
|
"agent_registration_already_in_progress": "Agent registration is already in progress. Please complete or cancel the current registration and try again.",
|
||||||
"auto_encode_mode": "Auto",
|
"auto_encode_mode": "Auto",
|
||||||
"auto_encode_mode_tooltip": "Encode the parameters in the request only if some special characters are present",
|
"auto_encode_mode_tooltip": "Encode the parameters in the request only if some special characters are present",
|
||||||
"background": "Background",
|
"background": "Background",
|
||||||
|
|
@ -928,6 +929,7 @@
|
||||||
"proxy_url": "Proxy URL",
|
"proxy_url": "Proxy URL",
|
||||||
"proxy_use_toggle": "Use the proxy middleware to send requests",
|
"proxy_use_toggle": "Use the proxy middleware to send requests",
|
||||||
"read_the": "Read the",
|
"read_the": "Read the",
|
||||||
|
"register_agent": "Register Agent",
|
||||||
"reset_default": "Use Default Proxy",
|
"reset_default": "Use Default Proxy",
|
||||||
"short_codes": "Short codes",
|
"short_codes": "Short codes",
|
||||||
"short_codes_description": "Short codes which were created by you.",
|
"short_codes_description": "Short codes which were created by you.",
|
||||||
|
|
|
||||||
|
|
@ -6,51 +6,16 @@
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
!store.authKey.value &&
|
!store.authKey.value &&
|
||||||
(!store.isAgentRunning.value || !hasCheckedAgent)
|
store.hasInitiatedRegistration.value &&
|
||||||
|
store.hasCheckedAgent.value
|
||||||
"
|
"
|
||||||
class="flex flex-1 items-center space-x-2"
|
class="flex flex-1 items-center space-x-2"
|
||||||
>
|
|
||||||
<div class="relative flex-1 border border-divider rounded p-2">
|
|
||||||
<span>{{ t("settings.agent_not_running_short") }}</span>
|
|
||||||
</div>
|
|
||||||
<HoppButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.retry')"
|
|
||||||
:icon="IconRefresh"
|
|
||||||
outline
|
|
||||||
class="rounded"
|
|
||||||
@click="handleAgentCheck"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-else-if="!store.authKey.value && !hasInitiatedRegistration"
|
|
||||||
class="flex flex-1 items-center space-x-2"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="relative flex-1 border border-divider rounded p-2 text-accent"
|
|
||||||
>
|
|
||||||
<span>{{ t("settings.agent_running") }}</span>
|
|
||||||
</div>
|
|
||||||
<HoppButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.register')"
|
|
||||||
:icon="IconPlus"
|
|
||||||
outline
|
|
||||||
class="rounded"
|
|
||||||
@click="initiateRegistration"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-else-if="!store.authKey.value"
|
|
||||||
class="flex flex-1 items-center space-x-2"
|
|
||||||
>
|
>
|
||||||
<HoppSmartInput
|
<HoppSmartInput
|
||||||
v-model="registrationOTP"
|
v-model="store.registrationOTP.value"
|
||||||
:autofocus="false"
|
:autofocus="false"
|
||||||
:placeholder="' '"
|
:placeholder="' '"
|
||||||
:disabled="isRegistering"
|
:disabled="store.isRegistering.value"
|
||||||
:label="t('settings.enter_otp')"
|
:label="t('settings.enter_otp')"
|
||||||
input-styles="input floating-input"
|
input-styles="input floating-input"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
|
|
@ -59,38 +24,50 @@
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.confirm')"
|
:title="t('action.confirm')"
|
||||||
:icon="IconCheck"
|
:icon="IconCheck"
|
||||||
:loading="isRegistering"
|
:loading="store.isRegistering.value"
|
||||||
outline
|
outline
|
||||||
class="rounded"
|
class="rounded"
|
||||||
@click="register"
|
@click="register"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="flex relative flex-1 items-center space-x-2">
|
<div
|
||||||
|
v-else-if="store.maskedAuthKey.value"
|
||||||
|
class="flex relative flex-1 items-center space-x-2"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class="text-secondaryLight text-tiny absolute -top-2 left-2 px-1 bg-primary"
|
class="text-secondaryLight text-tiny absolute -top-2 left-2 px-1"
|
||||||
>{{ t("settings.agent_registered") }}</label
|
>{{ t("settings.agent_registered") }}</label
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="w-full p-2 border border-dividerLight rounded bg-primary text-secondaryDark cursor-text select-all"
|
class="w-full p-2 border border-dividerLight rounded bg-primary text-secondaryDark cursor-text select-all"
|
||||||
>
|
>
|
||||||
{{ maskedAuthKey }}
|
{{ store.maskedAuthKey.value }}
|
||||||
</div>
|
</div>
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('settings.agent_reset_registration')"
|
:title="t('settings.agent_discard_registration')"
|
||||||
:icon="iconClear"
|
:icon="IconClose"
|
||||||
outline
|
outline
|
||||||
class="rounded"
|
class="rounded"
|
||||||
@click="resetRegistration"
|
@click="resetRegistration"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<HoppButtonSecondary
|
||||||
|
:icon="IconPlus"
|
||||||
|
:label="t('settings.register_agent')"
|
||||||
|
outline
|
||||||
|
class="rounded"
|
||||||
|
@click="handleAgentCheck"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from "vue"
|
import { computed, onMounted } from "vue"
|
||||||
import { refAutoReset } from "@vueuse/core"
|
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { useToast } from "@composables/toast"
|
import { useToast } from "@composables/toast"
|
||||||
|
|
@ -98,76 +75,77 @@ import { KernelInterceptorAgentStore } from "~/platform/std/kernel-interceptors/
|
||||||
import { KernelInterceptorService } from "~/services/kernel-interceptor.service"
|
import { KernelInterceptorService } from "~/services/kernel-interceptor.service"
|
||||||
|
|
||||||
import IconPlus from "~icons/lucide/plus"
|
import IconPlus from "~icons/lucide/plus"
|
||||||
import IconRotateCCW from "~icons/lucide/rotate-ccw"
|
|
||||||
import IconCheck from "~icons/lucide/check"
|
import IconCheck from "~icons/lucide/check"
|
||||||
import IconRefresh from "~icons/lucide/refresh-cw"
|
import IconClose from "~icons/lucide/x"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const store = useService(KernelInterceptorAgentStore)
|
const store = useService(KernelInterceptorAgentStore)
|
||||||
const interceptorService = useService(KernelInterceptorService)
|
const interceptorService = useService(KernelInterceptorService)
|
||||||
const iconClear = refAutoReset<typeof IconRotateCCW | typeof IconCheck>(
|
|
||||||
IconRotateCCW,
|
|
||||||
1000
|
|
||||||
)
|
|
||||||
|
|
||||||
const isSelected = computed(
|
const isSelected = computed(
|
||||||
() => interceptorService.current.value?.id === "agent"
|
() => interceptorService.current.value?.id === "agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
const hasInitiatedRegistration = ref(false)
|
const handleAgentCheck = async () => {
|
||||||
const maskedAuthKey = ref("")
|
|
||||||
const hasCheckedAgent = ref(false)
|
|
||||||
const registrationOTP = ref(store.authKey.value ? null : "")
|
|
||||||
const isRegistering = ref(false)
|
|
||||||
|
|
||||||
async function handleAgentCheck() {
|
|
||||||
try {
|
try {
|
||||||
await store.checkAgentStatus()
|
await store.checkAgentStatus()
|
||||||
hasCheckedAgent.value = true
|
store.hasCheckedAgent.value = true
|
||||||
if (!store.isAgentRunning.value) {
|
if (!store.isAgentRunning.value) {
|
||||||
toast.error(t("settings.agent_not_running"))
|
toast.error(t("settings.agent_not_running"))
|
||||||
|
} else {
|
||||||
|
await initiateRegistration()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
hasCheckedAgent.value = false
|
store.hasCheckedAgent.value = false
|
||||||
toast.error(t("settings.agent_check_failed"))
|
toast.error(t("settings.agent_check_failed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initiateRegistration() {
|
const initiateRegistration = async () => {
|
||||||
try {
|
try {
|
||||||
await store.initiateRegistration()
|
await store.initiateRegistration()
|
||||||
hasInitiatedRegistration.value = true
|
store.hasInitiatedRegistration.value = true
|
||||||
} catch (e) {}
|
toast.success(t("settings.agent_running"))
|
||||||
}
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
async function register() {
|
if (e.message === "There is already an existing registration happening") {
|
||||||
if (!registrationOTP.value) return
|
toast.error(t("settings.agent_registration_already_in_progress"))
|
||||||
isRegistering.value = true
|
} else {
|
||||||
try {
|
toast.error(t("settings.agent_registration_failed"))
|
||||||
await store.verifyRegistration(registrationOTP.value)
|
}
|
||||||
await updateMaskedAuthKey()
|
}
|
||||||
toast.success(t("settings.agent_registration_successful"))
|
|
||||||
registrationOTP.value = ""
|
|
||||||
} catch (e) {
|
|
||||||
} finally {
|
|
||||||
isRegistering.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetRegistration() {
|
const register = async () => {
|
||||||
store.authKey.value = null
|
if (!store.registrationOTP.value) return
|
||||||
maskedAuthKey.value = ""
|
store.isRegistering.value = true
|
||||||
registrationOTP.value = ""
|
try {
|
||||||
hasInitiatedRegistration.value = false
|
await store.verifyRegistration(store.registrationOTP.value)
|
||||||
|
await updateMaskedAuthKey()
|
||||||
|
toast.success(t("settings.agent_registration_successful"))
|
||||||
|
store.registrationOTP.value = ""
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
store.isRegistering.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateMaskedAuthKey() {
|
const resetRegistration = async () => {
|
||||||
|
await store.resetAuthKey()
|
||||||
|
store.maskedAuthKey.value = ""
|
||||||
|
store.registrationOTP.value = ""
|
||||||
|
store.hasInitiatedRegistration.value = false
|
||||||
|
store.hasCheckedAgent.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateMaskedAuthKey = async () => {
|
||||||
if (!store.authKey.value) return
|
if (!store.authKey.value) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const registration = await store.fetchRegistrationInfo()
|
const registration = await store.fetchRegistrationInfo()
|
||||||
maskedAuthKey.value = registration.auth_key_hash
|
store.maskedAuthKey.value = registration.auth_key_hash
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,13 @@ export class KernelInterceptorAgentStore extends Service {
|
||||||
public authKey = ref<string | null>(null)
|
public authKey = ref<string | null>(null)
|
||||||
private sharedSecretB16 = ref<string | null>(null)
|
private sharedSecretB16 = ref<string | null>(null)
|
||||||
|
|
||||||
|
// AgentSubtitle component shared variables for unified display across multiple components
|
||||||
|
public hasInitiatedRegistration = ref(false)
|
||||||
|
public maskedAuthKey = ref("")
|
||||||
|
public hasCheckedAgent = ref(false)
|
||||||
|
public registrationOTP = ref(this.authKey.value ? null : "")
|
||||||
|
public isRegistering = ref(false)
|
||||||
|
|
||||||
override async onServiceInit() {
|
override async onServiceInit() {
|
||||||
const initResult = await Store.init()
|
const initResult = await Store.init()
|
||||||
if (E.isLeft(initResult)) {
|
if (E.isLeft(initResult)) {
|
||||||
|
|
@ -119,6 +126,12 @@ export class KernelInterceptorAgentStore extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async resetAuthKey(): Promise<void> {
|
||||||
|
this.authKey.value = null
|
||||||
|
this.sharedSecretB16.value = null
|
||||||
|
await this.persistStore()
|
||||||
|
}
|
||||||
|
|
||||||
private mergeSecurity(
|
private mergeSecurity(
|
||||||
...settings: (Required<InputDomainSetting>["security"] | undefined)[]
|
...settings: (Required<InputDomainSetting>["security"] | undefined)[]
|
||||||
): Required<InputDomainSetting>["security"] | undefined {
|
): Required<InputDomainSetting>["security"] | undefined {
|
||||||
|
|
@ -195,7 +208,7 @@ export class KernelInterceptorAgentStore extends Service {
|
||||||
)
|
)
|
||||||
|
|
||||||
if (response.data.message !== "Registration received and stored") {
|
if (response.data.message !== "Registration received and stored") {
|
||||||
throw new Error("Registration failed")
|
throw new Error(response.data.message ?? "Registration failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return otp
|
return otp
|
||||||
|
|
@ -251,6 +264,7 @@ export class KernelInterceptorAgentStore extends Service {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
this.authKey.value = null
|
this.authKey.value = null
|
||||||
|
await this.persistStore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw error
|
throw error
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue