mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 12:05:53 +00:00
fix: better offline handling
This commit is contained in:
@@ -72,7 +72,6 @@ import {
|
|||||||
ButtonStyled,
|
ButtonStyled,
|
||||||
CopyCode,
|
CopyCode,
|
||||||
defineMessages,
|
defineMessages,
|
||||||
injectNotificationManager,
|
|
||||||
Table,
|
Table,
|
||||||
type TableColumn,
|
type TableColumn,
|
||||||
useFormatDateTime,
|
useFormatDateTime,
|
||||||
@@ -86,10 +85,11 @@ import ConfirmRevokeSharedInstanceInviteModal from '@/components/ui/shared-insta
|
|||||||
import SharedInstanceInstallationSettingsControls from '@/components/ui/shared-instances/SharedInstanceInstallationSettingsControls.vue'
|
import SharedInstanceInstallationSettingsControls from '@/components/ui/shared-instances/SharedInstanceInstallationSettingsControls.vue'
|
||||||
import { config } from '@/config'
|
import { config } from '@/config'
|
||||||
import { type SharedInstanceInvite, unpublish_shared_instance } from '@/helpers/instance'
|
import { type SharedInstanceInvite, unpublish_shared_instance } from '@/helpers/instance'
|
||||||
|
import { useSharedInstanceErrors } from '@/helpers/shared-instance-errors'
|
||||||
import { injectInstanceSettings } from '@/providers/instance-settings'
|
import { injectInstanceSettings } from '@/providers/instance-settings'
|
||||||
|
|
||||||
const { instance, offline, onUnlinked } = injectInstanceSettings()
|
const { instance, offline, onUnlinked } = injectInstanceSettings()
|
||||||
const { handleError } = injectNotificationManager()
|
const { notifySharedInstanceError } = useSharedInstanceErrors()
|
||||||
const { formatMessage } = useVIntl()
|
const { formatMessage } = useVIntl()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const unpublishing = ref(false)
|
const unpublishing = ref(false)
|
||||||
@@ -162,7 +162,7 @@ async function unpublishSharedInstance() {
|
|||||||
await queryClient.invalidateQueries({ queryKey: ['linkedModpackInfo', instance.value.id] })
|
await queryClient.invalidateQueries({ queryKey: ['linkedModpackInfo', instance.value.id] })
|
||||||
onUnlinked()
|
onUnlinked()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error)
|
notifySharedInstanceError(error)
|
||||||
} finally {
|
} finally {
|
||||||
unpublishing.value = false
|
unpublishing.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -19,6 +19,7 @@ import {
|
|||||||
install_shared_instance,
|
install_shared_instance,
|
||||||
} from '@/helpers/install'
|
} from '@/helpers/install'
|
||||||
import { list } from '@/helpers/instance'
|
import { list } from '@/helpers/instance'
|
||||||
|
import { useSharedInstanceErrors } from '@/helpers/shared-instance-errors'
|
||||||
import { useTheming } from '@/store/state'
|
import { useTheming } from '@/store/state'
|
||||||
|
|
||||||
import { parseSharedInstanceInviteNotification } from './shared-instance-invite-parser'
|
import { parseSharedInstanceInviteNotification } from './shared-instance-invite-parser'
|
||||||
@@ -47,6 +48,8 @@ export function useSharedInstanceInviteHandler(
|
|||||||
const auth = injectAuth()
|
const auth = injectAuth()
|
||||||
const client = injectModrinthClient()
|
const client = injectModrinthClient()
|
||||||
const { handleError } = injectNotificationManager()
|
const { handleError } = injectNotificationManager()
|
||||||
|
const { notifySharedInstanceConnectionError, notifySharedInstanceError } =
|
||||||
|
useSharedInstanceErrors()
|
||||||
const popupNotificationManager = injectPopupNotificationManager()
|
const popupNotificationManager = injectPopupNotificationManager()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -78,7 +81,10 @@ export function useSharedInstanceInviteHandler(
|
|||||||
!invite.invitedByUsername && invite.invitedById
|
!invite.invitedByUsername && invite.invitedById
|
||||||
? get_user(invite.invitedById, 'bypass').catch(() => null)
|
? get_user(invite.invitedById, 'bypass').catch(() => null)
|
||||||
: null,
|
: null,
|
||||||
client.sharedinstances.instances_v1.get(invite.sharedInstanceId).catch(() => null),
|
client.sharedinstances.instances_v1.get(invite.sharedInstanceId).catch(() => {
|
||||||
|
notifySharedInstanceConnectionError()
|
||||||
|
return null
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -177,7 +183,7 @@ export function useSharedInstanceInviteHandler(
|
|||||||
() => markNotificationRead(notification),
|
() => markNotificationRead(notification),
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(toError(error))
|
notifySharedInstanceError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +273,7 @@ export function useSharedInstanceInviteHandler(
|
|||||||
await queryClient.invalidateQueries({ queryKey: ['instances'] })
|
await queryClient.invalidateQueries({ queryKey: ['instances'] })
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(toError(error))
|
notifySharedInstanceError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ export interface SharedInstanceUpdateDiff {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SHARED_INSTANCE_UNAVAILABLE_ERROR_CODE = 'shared_instance_unavailable'
|
export const SHARED_INSTANCE_UNAVAILABLE_ERROR_CODE = 'shared_instance_unavailable'
|
||||||
|
export const SHARED_INSTANCES_API_ERROR_CODE = 'shared_instances_api_error'
|
||||||
|
|
||||||
export type SharedInstanceUnavailableReason = 'deleted' | 'access_revoked' | 'quarantined'
|
export type SharedInstanceUnavailableReason = 'deleted' | 'access_revoked' | 'quarantined'
|
||||||
|
|
||||||
@@ -113,6 +114,10 @@ export function isSharedInstanceUnavailableError(error: unknown) {
|
|||||||
return getSharedInstanceUnavailableReason(error) !== null
|
return getSharedInstanceUnavailableReason(error) !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isSharedInstancesApiError(error: unknown) {
|
||||||
|
return isRecord(error) && error.code === SHARED_INSTANCES_API_ERROR_CODE
|
||||||
|
}
|
||||||
|
|
||||||
export function getSharedInstanceUnavailableReason(
|
export function getSharedInstanceUnavailableReason(
|
||||||
error: unknown,
|
error: unknown,
|
||||||
): SharedInstanceUnavailableReason | null {
|
): SharedInstanceUnavailableReason | null {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
|
import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
|
||||||
|
|
||||||
import { getErrorMessage, type SharedInstanceUnavailableReason } from '@/helpers/install'
|
import {
|
||||||
|
getErrorMessage,
|
||||||
|
isSharedInstancesApiError,
|
||||||
|
type SharedInstanceUnavailableReason,
|
||||||
|
} from '@/helpers/install'
|
||||||
|
|
||||||
export const sharedInstanceErrorMessages = defineMessages({
|
export const sharedInstanceErrorMessages = defineMessages({
|
||||||
unavailableTitle: {
|
unavailableTitle: {
|
||||||
@@ -39,6 +43,14 @@ export const sharedInstanceErrorMessages = defineMessages({
|
|||||||
id: 'instance.shared-instance.error.title',
|
id: 'instance.shared-instance.error.title',
|
||||||
defaultMessage: 'Something has gone wrong',
|
defaultMessage: 'Something has gone wrong',
|
||||||
},
|
},
|
||||||
|
networkErrorTitle: {
|
||||||
|
id: 'instance.shared-instance.network-error.title',
|
||||||
|
defaultMessage: 'Network error',
|
||||||
|
},
|
||||||
|
networkErrorText: {
|
||||||
|
id: 'instance.shared-instance.network-error.text',
|
||||||
|
defaultMessage: 'Unable to connect to shared instances API',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export function sharedInstanceUnavailableTextMessage(
|
export function sharedInstanceUnavailableTextMessage(
|
||||||
@@ -82,7 +94,20 @@ export function useSharedInstanceErrors() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notifySharedInstanceConnectionError() {
|
||||||
|
addNotification({
|
||||||
|
type: 'error',
|
||||||
|
title: formatMessage(sharedInstanceErrorMessages.networkErrorTitle),
|
||||||
|
text: formatMessage(sharedInstanceErrorMessages.networkErrorText),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function notifySharedInstanceError(error: unknown) {
|
function notifySharedInstanceError(error: unknown) {
|
||||||
|
if (isSharedInstancesApiError(error)) {
|
||||||
|
notifySharedInstanceConnectionError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
addNotification({
|
addNotification({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: formatMessage(sharedInstanceErrorMessages.errorTitle),
|
title: formatMessage(sharedInstanceErrorMessages.errorTitle),
|
||||||
@@ -92,6 +117,7 @@ export function useSharedInstanceErrors() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
formatSharedInstanceUnavailable,
|
formatSharedInstanceUnavailable,
|
||||||
|
notifySharedInstanceConnectionError,
|
||||||
notifySharedInstanceError,
|
notifySharedInstanceError,
|
||||||
notifySharedInstanceUnavailable,
|
notifySharedInstanceUnavailable,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -590,17 +590,16 @@ const startInstance = async (context: string) => {
|
|||||||
!!instance.value.shared_instance && !sharedInstanceActionsLocked.value && !offline.value
|
!!instance.value.shared_instance && !sharedInstanceActionsLocked.value && !offline.value
|
||||||
|
|
||||||
if (canCheckSharedInstanceUpdate) {
|
if (canCheckSharedInstanceUpdate) {
|
||||||
let preview: Awaited<ReturnType<typeof refreshSharedInstanceUpdatePreview>>
|
let preview: Awaited<ReturnType<typeof refreshSharedInstanceUpdatePreview>> = null
|
||||||
checkingSharedInstanceLaunch.value = true
|
checkingSharedInstanceLaunch.value = true
|
||||||
try {
|
try {
|
||||||
preview = await refreshSharedInstanceUpdatePreview()
|
preview = await refreshSharedInstanceUpdatePreview()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isSharedInstanceUnavailableError(error)) {
|
if (isSharedInstanceUnavailableError(error)) {
|
||||||
await handleSharedInstanceUnavailable(getSharedInstanceUnavailableReason(error))
|
await handleSharedInstanceUnavailable(getSharedInstanceUnavailableReason(error))
|
||||||
} else {
|
return
|
||||||
notifySharedInstanceError(error)
|
|
||||||
}
|
}
|
||||||
return
|
notifySharedInstanceError(error)
|
||||||
} finally {
|
} finally {
|
||||||
checkingSharedInstanceLaunch.value = false
|
checkingSharedInstanceLaunch.value = false
|
||||||
}
|
}
|
||||||
@@ -707,7 +706,7 @@ async function reportSharedInstance(event?: MouseEvent, closeUpdateModal = false
|
|||||||
if (closeUpdateModal) sharedInstanceUpdateModal.value?.hide()
|
if (closeUpdateModal) sharedInstanceUpdateModal.value?.hide()
|
||||||
sharedInstanceReportModal.value?.showReport(preview, event)
|
sharedInstanceReportModal.value?.showReport(preview, event)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error as Error)
|
notifySharedInstanceError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,13 @@
|
|||||||
@state-change="publishState = $event"
|
@state-change="publishState = $event"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="membersTableLoading" class="h-64" aria-hidden="true" />
|
<SharedInstanceShareEmptyState
|
||||||
|
v-if="unableToConnect"
|
||||||
|
:heading="formatMessage(messages.unableToConnectHeading)"
|
||||||
|
:description="formatMessage(messages.unableToConnectDescription)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-else-if="membersTableLoading" class="h-64" aria-hidden="true" />
|
||||||
|
|
||||||
<SharedInstanceMembersTable
|
<SharedInstanceMembersTable
|
||||||
v-else-if="showMembersTable"
|
v-else-if="showMembersTable"
|
||||||
@@ -150,7 +156,7 @@ import {
|
|||||||
type InvitePlayersUser,
|
type InvitePlayersUser,
|
||||||
useVIntl,
|
useVIntl,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
import { useQueryClient } from '@tanstack/vue-query'
|
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||||
import { computed, ref, toRef, watch } from 'vue'
|
import { computed, ref, toRef, watch } from 'vue'
|
||||||
|
|
||||||
@@ -158,9 +164,10 @@ import ModrinthAccountRequiredModal from '@/components/ui/modal/ModrinthAccountR
|
|||||||
import SharedInstancePublishModal from '@/components/ui/shared-instances/SharedInstancePublishModal.vue'
|
import SharedInstancePublishModal from '@/components/ui/shared-instances/SharedInstancePublishModal.vue'
|
||||||
import {
|
import {
|
||||||
getSharedInstanceUnavailableReason,
|
getSharedInstanceUnavailableReason,
|
||||||
|
isSharedInstancesApiError,
|
||||||
isSharedInstanceUnavailableError,
|
isSharedInstanceUnavailableError,
|
||||||
} from '@/helpers/install'
|
} from '@/helpers/install'
|
||||||
import { edit } from '@/helpers/instance'
|
import { can_current_user_use_shared_instances, edit } from '@/helpers/instance'
|
||||||
import type { ModrinthAuthFlow } from '@/helpers/mr_auth.ts'
|
import type { ModrinthAuthFlow } from '@/helpers/mr_auth.ts'
|
||||||
import {
|
import {
|
||||||
sharedInstanceErrorMessages,
|
sharedInstanceErrorMessages,
|
||||||
@@ -196,6 +203,7 @@ const actionsLocked = sharedInstanceState.shareActionsLocked
|
|||||||
const sharedInstanceActionsLocked = actionsLocked
|
const sharedInstanceActionsLocked = actionsLocked
|
||||||
const currentUserId = computed(() => auth.user.value?.id ?? null)
|
const currentUserId = computed(() => auth.user.value?.id ?? null)
|
||||||
const isSignedIn = computed(() => !!auth.session_token.value)
|
const isSignedIn = computed(() => !!auth.session_token.value)
|
||||||
|
const sharedInstancesApiUnavailable = ref(false)
|
||||||
const accountRequiredModal = ref<InstanceType<typeof ModrinthAccountRequiredModal>>()
|
const accountRequiredModal = ref<InstanceType<typeof ModrinthAccountRequiredModal>>()
|
||||||
const invitePlayersModal = ref<InstanceType<typeof InvitePlayersModal>>()
|
const invitePlayersModal = ref<InstanceType<typeof InvitePlayersModal>>()
|
||||||
const unlinkModal = ref<InstanceType<typeof ConfirmUnlinkModal>>()
|
const unlinkModal = ref<InstanceType<typeof ConfirmUnlinkModal>>()
|
||||||
@@ -212,10 +220,22 @@ function notifyOperationError(error: unknown) {
|
|||||||
sharedInstanceState.unavailableManager.value,
|
sharedInstanceState.unavailableManager.value,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
if (isSharedInstancesApiError(error)) sharedInstancesApiUnavailable.value = true
|
||||||
notifySharedInstanceError(error)
|
notifySharedInstanceError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eligibilityQuery = useQuery({
|
||||||
|
queryKey: computed(() => ['shared-instance-eligibility', currentUserId.value]),
|
||||||
|
queryFn: can_current_user_use_shared_instances,
|
||||||
|
enabled: () => isSignedIn.value && !!currentUserId.value,
|
||||||
|
retry: false,
|
||||||
|
staleTime: Infinity,
|
||||||
|
refetchOnMount: 'always',
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnReconnect: false,
|
||||||
|
})
|
||||||
|
|
||||||
const members = useSharedInstanceMembers({
|
const members = useSharedInstanceMembers({
|
||||||
instance,
|
instance,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
@@ -258,6 +278,12 @@ const lockedActionButton = computed(() =>
|
|||||||
const sharedInstanceUnavailableReason = sharedInstanceState.unavailableReason
|
const sharedInstanceUnavailableReason = sharedInstanceState.unavailableReason
|
||||||
const sharedInstanceUnavailable = computed(() => !!sharedInstanceUnavailableReason.value)
|
const sharedInstanceUnavailable = computed(() => !!sharedInstanceUnavailableReason.value)
|
||||||
const sharedInstanceUnavailableManager = sharedInstanceState.unavailableManager
|
const sharedInstanceUnavailableManager = sharedInstanceState.unavailableManager
|
||||||
|
const unableToConnect = computed(
|
||||||
|
() =>
|
||||||
|
sharedInstancesApiUnavailable.value ||
|
||||||
|
isSharedInstancesApiError(eligibilityQuery.error.value) ||
|
||||||
|
isSharedInstancesApiError(members.query.error.value),
|
||||||
|
)
|
||||||
const membersTableLoading = computed(
|
const membersTableLoading = computed(
|
||||||
() =>
|
() =>
|
||||||
members.rows.value.length === 0 &&
|
members.rows.value.length === 0 &&
|
||||||
@@ -289,6 +315,15 @@ const importedModpackBackupTip = computed(() =>
|
|||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
signInButton: { id: 'app.instance.share.sign-in.button', defaultMessage: 'Sign in' },
|
signInButton: { id: 'app.instance.share.sign-in.button', defaultMessage: 'Sign in' },
|
||||||
|
unableToConnectHeading: {
|
||||||
|
id: 'app.instance.share.unable-to-connect.heading',
|
||||||
|
defaultMessage: 'Unable to connect',
|
||||||
|
},
|
||||||
|
unableToConnectDescription: {
|
||||||
|
id: 'app.instance.share.unable-to-connect.description',
|
||||||
|
defaultMessage:
|
||||||
|
'The shared instances service is not accessible at the moment, please try again later',
|
||||||
|
},
|
||||||
noFriendsInvitedHeading: {
|
noFriendsInvitedHeading: {
|
||||||
id: 'app.instance.share.empty.heading',
|
id: 'app.instance.share.empty.heading',
|
||||||
defaultMessage: 'No friends invited',
|
defaultMessage: 'No friends invited',
|
||||||
@@ -396,6 +431,23 @@ function signInToShare(event?: MouseEvent) {
|
|||||||
void accountRequiredModal.value?.show(event)
|
void accountRequiredModal.value?.show(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[eligibilityQuery.error, members.query.error],
|
||||||
|
(errors) => {
|
||||||
|
for (const error of errors) {
|
||||||
|
if (isSharedInstancesApiError(error)) notifyOperationError(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
[eligibilityQuery.data, members.query.data],
|
||||||
|
([eligibility, memberRows]) => {
|
||||||
|
if (eligibility !== undefined && memberRows !== undefined) {
|
||||||
|
sharedInstancesApiUnavailable.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
watch(
|
watch(
|
||||||
() => props.instance.id,
|
() => props.instance.id,
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export function useSharedInstanceMembers(options: {
|
|||||||
queryFn: ({ queryKey }) => fetchRows(queryKey),
|
queryFn: ({ queryKey }) => fetchRows(queryKey),
|
||||||
enabled: () =>
|
enabled: () =>
|
||||||
options.isSignedIn.value && !!options.instance.value.id && !options.actionsLocked.value,
|
options.isSignedIn.value && !!options.instance.value.id && !options.actionsLocked.value,
|
||||||
|
retry: false,
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
refetchOnMount: 'always',
|
refetchOnMount: 'always',
|
||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
|
|||||||
+13
-2
@@ -91,14 +91,25 @@ macro_rules! impl_serialize {
|
|||||||
theseus::ErrorKind::SharedInstanceUnavailable(reason) => Some(reason),
|
theseus::ErrorKind::SharedInstanceUnavailable(reason) => Some(reason),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
let code = match theseus_error.raw.as_ref() {
|
||||||
|
theseus::ErrorKind::SharedInstanceUnavailable(_) => {
|
||||||
|
Some("shared_instance_unavailable")
|
||||||
|
}
|
||||||
|
theseus::ErrorKind::SharedInstancesApiError(_) => {
|
||||||
|
Some("shared_instances_api_error")
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
let mut state = serializer.serialize_struct(
|
let mut state = serializer.serialize_struct(
|
||||||
"Theseus",
|
"Theseus",
|
||||||
if unavailable_reason.is_some() { 4 } else { 2 },
|
2 + usize::from(code.is_some()) + usize::from(unavailable_reason.is_some()),
|
||||||
)?;
|
)?;
|
||||||
state.serialize_field("field_name", "Theseus")?;
|
state.serialize_field("field_name", "Theseus")?;
|
||||||
state.serialize_field("message", &theseus_error.to_string())?;
|
state.serialize_field("message", &theseus_error.to_string())?;
|
||||||
|
if let Some(code) = code {
|
||||||
|
state.serialize_field("code", code)?;
|
||||||
|
}
|
||||||
if let Some(reason) = unavailable_reason {
|
if let Some(reason) = unavailable_reason {
|
||||||
state.serialize_field("code", "shared_instance_unavailable")?;
|
|
||||||
state.serialize_field("reason", reason)?;
|
state.serialize_field("reason", reason)?;
|
||||||
}
|
}
|
||||||
state.end()
|
state.end()
|
||||||
|
|||||||
@@ -713,9 +713,17 @@ where
|
|||||||
let body = match response.text().await {
|
let body = match response.text().await {
|
||||||
Ok(body) => body,
|
Ok(body) => body,
|
||||||
Err(error) if strip_response_url => {
|
Err(error) if strip_response_url => {
|
||||||
return Err(error.without_url().into());
|
return Err(crate::ErrorKind::SharedInstancesApiError(
|
||||||
|
error.without_url().to_string(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::ErrorKind::SharedInstancesApiError(
|
||||||
|
error.to_string(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
Err(error) => return Err(error.into()),
|
|
||||||
};
|
};
|
||||||
serde_json::from_str::<T>(&body).map_err(|error| {
|
serde_json::from_str::<T>(&body).map_err(|error| {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
@@ -729,7 +737,7 @@ where
|
|||||||
error_column = error.column(),
|
error_column = error.column(),
|
||||||
"Shared instances API returned an invalid JSON response"
|
"Shared instances API returned an invalid JSON response"
|
||||||
);
|
);
|
||||||
crate::ErrorKind::OtherError(format!(
|
crate::ErrorKind::SharedInstancesApiError(format!(
|
||||||
"Shared instances API request {operation} {method} {log_path} returned invalid JSON with status {status}"
|
"Shared instances API request {operation} {method} {log_path} returned invalid JSON with status {status}"
|
||||||
))
|
))
|
||||||
.into()
|
.into()
|
||||||
@@ -870,7 +878,12 @@ pub(super) async fn send_bytes_request_to_url(
|
|||||||
.header(reqwest::header::CONTENT_TYPE, "application/octet-stream")
|
.header(reqwest::header::CONTENT_TYPE, "application/octet-stream")
|
||||||
.body(body)
|
.body(body)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(|error| {
|
||||||
|
crate::ErrorKind::SharedInstancesApiError(
|
||||||
|
error.without_url().to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
if response.status().is_success() {
|
if response.status().is_success() {
|
||||||
let request_id = response_request_id(&response);
|
let request_id = response_request_id(&response);
|
||||||
@@ -960,9 +973,17 @@ async fn send_request_with_auth_and_log_path(
|
|||||||
let response = match request.send().await {
|
let response = match request.send().await {
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(error) if path != log_path => {
|
Err(error) if path != log_path => {
|
||||||
return Err(error.without_url().into());
|
return Err(crate::ErrorKind::SharedInstancesApiError(
|
||||||
|
error.without_url().to_string(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::ErrorKind::SharedInstancesApiError(
|
||||||
|
error.to_string(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
Err(error) => return Err(error.into()),
|
|
||||||
};
|
};
|
||||||
if response.status().is_success() {
|
if response.status().is_success() {
|
||||||
let request_id = response_request_id(&response);
|
let request_id = response_request_id(&response);
|
||||||
@@ -995,10 +1016,14 @@ pub(super) async fn shared_instances_request_error<T>(
|
|||||||
request_id = request_id.as_deref().unwrap_or("none"),
|
request_id = request_id.as_deref().unwrap_or("none"),
|
||||||
"Shared instances API request failed"
|
"Shared instances API request failed"
|
||||||
);
|
);
|
||||||
Err(crate::ErrorKind::OtherError(format!(
|
let message = format!(
|
||||||
"Shared instances API request {operation} {method} {path} failed with status {status}"
|
"Shared instances API request {operation} {method} {path} failed with status {status}"
|
||||||
))
|
);
|
||||||
.into())
|
if status.is_server_error() {
|
||||||
|
return Err(crate::ErrorKind::SharedInstancesApiError(message).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(crate::ErrorKind::OtherError(message).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn response_request_id(
|
pub(super) fn response_request_id(
|
||||||
|
|||||||
@@ -244,13 +244,7 @@ pub(crate) async fn check_shared_instance_availability_before_launch(
|
|||||||
let availability =
|
let availability =
|
||||||
match get_remote_instance_access(&attachment.id, state).await {
|
match get_remote_instance_access(&attachment.id, state).await {
|
||||||
Ok(availability) => availability,
|
Ok(availability) => availability,
|
||||||
Err(error)
|
Err(error) => {
|
||||||
if matches!(
|
|
||||||
error.raw.as_ref(),
|
|
||||||
crate::ErrorKind::NoCredentialsError
|
|
||||||
| crate::ErrorKind::FetchError(_)
|
|
||||||
) =>
|
|
||||||
{
|
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
instance_id,
|
instance_id,
|
||||||
shared_instance_id = %attachment.id,
|
shared_instance_id = %attachment.id,
|
||||||
@@ -259,7 +253,6 @@ pub(crate) async fn check_shared_instance_availability_before_launch(
|
|||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(error) => return Err(error),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let SharedInstanceRemoteResponse::Unavailable(reason) = availability {
|
if let SharedInstanceRemoteResponse::Unavailable(reason) = availability {
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ pub enum ErrorKind {
|
|||||||
#[error("Shared instance unavailable: {0}")]
|
#[error("Shared instance unavailable: {0}")]
|
||||||
SharedInstanceUnavailable(SharedInstanceUnavailableReason),
|
SharedInstanceUnavailable(SharedInstanceUnavailableReason),
|
||||||
|
|
||||||
|
#[error("Shared instances API request failed: {0}")]
|
||||||
|
SharedInstancesApiError(String),
|
||||||
|
|
||||||
#[error("Join handle error: {0}")]
|
#[error("Join handle error: {0}")]
|
||||||
JoinError(#[from] tokio::task::JoinError),
|
JoinError(#[from] tokio::task::JoinError),
|
||||||
|
|
||||||
|
|||||||
@@ -1293,6 +1293,7 @@ fn install_error_code(
|
|||||||
ErrorKind::SharedInstanceUnavailable(_) => {
|
ErrorKind::SharedInstanceUnavailable(_) => {
|
||||||
"shared_instance_unavailable"
|
"shared_instance_unavailable"
|
||||||
}
|
}
|
||||||
|
ErrorKind::SharedInstancesApiError(_) => "shared_instances_api_error",
|
||||||
ErrorKind::InputError(_) => match phase {
|
ErrorKind::InputError(_) => match phase {
|
||||||
PreparingInstance | Finalizing => "instance_error",
|
PreparingInstance | Finalizing => "instance_error",
|
||||||
ResolvingPack | DownloadingPackFile | ReadingPackManifest => {
|
ResolvingPack | DownloadingPackFile | ReadingPackManifest => {
|
||||||
|
|||||||
Reference in New Issue
Block a user