Show when authentication is bad (cookie header only)
This commit is contained in:
@ -1,6 +1,8 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
EngineConnectionStateType,
|
||||||
|
DisconnectingType,
|
||||||
EngineCommandManagerEvents,
|
EngineCommandManagerEvents,
|
||||||
EngineConnectionEvents,
|
EngineConnectionEvents,
|
||||||
ConnectionError,
|
ConnectionError,
|
||||||
@ -13,8 +15,12 @@ const Loading = ({ children }: React.PropsWithChildren) => {
|
|||||||
const [error, setError] = useState<ConnectionError>(ConnectionError.Unset)
|
const [error, setError] = useState<ConnectionError>(ConnectionError.Unset)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onConnectionStateChange = (state: EngineConnectionState) => {
|
const onConnectionStateChange = ({ detail: state }: CustomEvent) => {
|
||||||
console.log("<Loading/>", state)
|
if (
|
||||||
|
(state.type !== EngineConnectionStateType.Disconnected
|
||||||
|
|| state.type !== EngineConnectionStateType.Disconnecting)
|
||||||
|
&& state.value?.type !== DisconnectingType.Error) return
|
||||||
|
setError(state.value.value.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onEngineAvailable = ({ detail: engineConnection }: CustomEvent) => {
|
const onEngineAvailable = ({ detail: engineConnection }: CustomEvent) => {
|
||||||
|
@ -56,11 +56,11 @@ export function useNetworkStatus() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onlineCallback = () => {
|
const onlineCallback = () => {
|
||||||
setSteps(initialConnectingTypeGroupState)
|
|
||||||
setInternetConnected(true)
|
setInternetConnected(true)
|
||||||
}
|
}
|
||||||
const offlineCallback = () => {
|
const offlineCallback = () => {
|
||||||
setInternetConnected(false)
|
setInternetConnected(false)
|
||||||
|
setSteps(structuredClone(initialConnectingTypeGroupState))
|
||||||
}
|
}
|
||||||
window.addEventListener('online', onlineCallback)
|
window.addEventListener('online', onlineCallback)
|
||||||
window.addEventListener('offline', offlineCallback)
|
window.addEventListener('offline', offlineCallback)
|
||||||
@ -70,10 +70,6 @@ export function useNetworkStatus() {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log("pingPongHealth", pingPongHealth)
|
|
||||||
}, [pingPongHealth])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const issues = {
|
const issues = {
|
||||||
[ConnectingTypeGroup.WebSocket]: steps[
|
[ConnectingTypeGroup.WebSocket]: steps[
|
||||||
|
@ -43,7 +43,7 @@ export function useSetupEngineManager(
|
|||||||
engineCommandManager.pool = settings.pool
|
engineCommandManager.pool = settings.pool
|
||||||
}
|
}
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
const startEngineInstance = () => {
|
||||||
// Load the engine command manager once with the initial width and height,
|
// Load the engine command manager once with the initial width and height,
|
||||||
// then we do not want to reload it.
|
// then we do not want to reload it.
|
||||||
const { width: quadWidth, height: quadHeight } = getDimensions(
|
const { width: quadWidth, height: quadHeight } = getDimensions(
|
||||||
@ -73,7 +73,9 @@ export function useSetupEngineManager(
|
|||||||
})
|
})
|
||||||
hasSetNonZeroDimensions.current = true
|
hasSetNonZeroDimensions.current = true
|
||||||
}
|
}
|
||||||
}, [streamRef?.current?.offsetWidth, streamRef?.current?.offsetHeight])
|
}
|
||||||
|
|
||||||
|
useLayoutEffect(startEngineInstance, [streamRef?.current?.offsetWidth, streamRef?.current?.offsetHeight])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleResize = deferExecution(() => {
|
const handleResize = deferExecution(() => {
|
||||||
@ -96,8 +98,20 @@ export function useSetupEngineManager(
|
|||||||
}
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
|
const onOnline = () => {
|
||||||
|
startEngineInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOffline = () => {
|
||||||
|
engineCommandManager.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('online', onOnline)
|
||||||
|
window.addEventListener('offline', onOffline)
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
return () => {
|
return () => {
|
||||||
|
window.removeEventListener('online', onOnline)
|
||||||
|
window.removeEventListener('offline', onOffline)
|
||||||
window.removeEventListener('resize', handleResize)
|
window.removeEventListener('resize', handleResize)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { PathToNode, Program, SourceRange } from 'lang/wasm'
|
import { PathToNode, Program, SourceRange } from 'lang/wasm'
|
||||||
import { VITE_KC_API_WS_MODELING_URL } from 'env'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_API_BASE_URL } from 'env'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { exportSave } from 'lib/exportSave'
|
import { exportSave } from 'lib/exportSave'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
import withBaseURL from 'lib/withBaseURL'
|
||||||
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
|
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
|
||||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||||
@ -140,7 +143,7 @@ export const CONNECTION_ERROR_TEXT: Record<ConnectionError, string> = {
|
|||||||
[ConnectionError.DataChannelError]: "The data channel signaled an error.",
|
[ConnectionError.DataChannelError]: "The data channel signaled an error.",
|
||||||
[ConnectionError.WebSocketError]: "The websocket signaled an error.",
|
[ConnectionError.WebSocketError]: "The websocket signaled an error.",
|
||||||
[ConnectionError.LocalDescriptionInvalid]: "The local description is invalid.",
|
[ConnectionError.LocalDescriptionInvalid]: "The local description is invalid.",
|
||||||
[ConnectionError.BadAuthToken]: "Your authorization token is not valid; please login again.",
|
[ConnectionError.BadAuthToken]: "Your authorization token is invalid; please login again.",
|
||||||
[ConnectionError.TooManyConnections]: "There are too many connections.",
|
[ConnectionError.TooManyConnections]: "There are too many connections.",
|
||||||
[ConnectionError.Unknown]: "An unexpected error occurred. Please report this to us.",
|
[ConnectionError.Unknown]: "An unexpected error occurred. Please report this to us.",
|
||||||
}
|
}
|
||||||
@ -150,7 +153,7 @@ export interface ErrorType {
|
|||||||
error: ConnectionError,
|
error: ConnectionError,
|
||||||
|
|
||||||
// Additional context.
|
// Additional context.
|
||||||
context?: string,
|
context?: any,
|
||||||
|
|
||||||
// We assign this in the state setter because we may have not failed at
|
// We assign this in the state setter because we may have not failed at
|
||||||
// a Connecting state, which we check for there.
|
// a Connecting state, which we check for there.
|
||||||
@ -276,10 +279,11 @@ class EngineConnection extends EventTarget {
|
|||||||
if (next.type === EngineConnectionStateType.Disconnecting) {
|
if (next.type === EngineConnectionStateType.Disconnecting) {
|
||||||
const sub = next.value
|
const sub = next.value
|
||||||
if (sub.type === DisconnectingType.Error) {
|
if (sub.type === DisconnectingType.Error) {
|
||||||
|
console.log(sub)
|
||||||
|
|
||||||
// Record the last step we failed at.
|
// Record the last step we failed at.
|
||||||
// (Check the current state that we're about to override that
|
// (Check the current state that we're about to override that
|
||||||
// it was a Connecting state.)
|
// it was a Connecting state.)
|
||||||
console.log(sub)
|
|
||||||
if (this._state.type === EngineConnectionStateType.Connecting) {
|
if (this._state.type === EngineConnectionStateType.Connecting) {
|
||||||
if (!sub.value) sub.value = {}
|
if (!sub.value) sub.value = {}
|
||||||
sub.value.lastConnectingValue = this._state.value
|
sub.value.lastConnectingValue = this._state.value
|
||||||
@ -366,13 +370,18 @@ class EngineConnection extends EventTarget {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Information on the connect transaction
|
|
||||||
|
|
||||||
const createPeerConnection = () => {
|
const createPeerConnection = () => {
|
||||||
this.pc = new RTCPeerConnection({
|
this.pc = new RTCPeerConnection({
|
||||||
bundlePolicy: 'max-bundle',
|
bundlePolicy: 'max-bundle',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Other parts of the application expect pc to be initialized when firing.
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent(EngineConnectionEvents.ConnectionStarted, {
|
||||||
|
detail: this,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
// Data channels MUST BE specified before SDP offers because requesting
|
// Data channels MUST BE specified before SDP offers because requesting
|
||||||
// them affects what our needs are!
|
// them affects what our needs are!
|
||||||
const DATACHANNEL_NAME_UMC = 'unreliable_modeling_cmds'
|
const DATACHANNEL_NAME_UMC = 'unreliable_modeling_cmds'
|
||||||
@ -440,7 +449,7 @@ class EngineConnection extends EventTarget {
|
|||||||
type: DisconnectingType.Error,
|
type: DisconnectingType.Error,
|
||||||
value: {
|
value: {
|
||||||
error: ConnectionError.ICENegotiate,
|
error: ConnectionError.ICENegotiate,
|
||||||
context: event.toString(),
|
context: event,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -566,7 +575,6 @@ class EngineConnection extends EventTarget {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.unreliableDataChannel.addEventListener('close', (event) => {
|
this.unreliableDataChannel.addEventListener('close', (event) => {
|
||||||
console.log("data channel close")
|
|
||||||
|
|
||||||
this.disconnectAll()
|
this.disconnectAll()
|
||||||
this.finalizeIfAllConnectionsClosed()
|
this.finalizeIfAllConnectionsClosed()
|
||||||
@ -581,7 +589,7 @@ class EngineConnection extends EventTarget {
|
|||||||
type: DisconnectingType.Error,
|
type: DisconnectingType.Error,
|
||||||
value: {
|
value: {
|
||||||
error: ConnectionError.DataChannelError,
|
error: ConnectionError.DataChannelError,
|
||||||
context: event.toString(),
|
context: event,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -619,6 +627,7 @@ class EngineConnection extends EventTarget {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createWebSocketConnection = () => {
|
||||||
this.websocket = new WebSocket(this.url, [])
|
this.websocket = new WebSocket(this.url, [])
|
||||||
this.websocket.binaryType = 'arraybuffer'
|
this.websocket.binaryType = 'arraybuffer'
|
||||||
|
|
||||||
@ -638,16 +647,11 @@ class EngineConnection extends EventTarget {
|
|||||||
// Otherwise when run in a browser, the token is sent implicitly via
|
// Otherwise when run in a browser, the token is sent implicitly via
|
||||||
// the Cookie header.
|
// the Cookie header.
|
||||||
if (this.token) {
|
if (this.token) {
|
||||||
this.send({
|
this.send({ headers: { Authorization: `Bearer ${this.token}` } })
|
||||||
type: 'headers',
|
|
||||||
headers: { Authorization: `Bearer ${this.token}` },
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.websocket.addEventListener('close', (event) => {
|
this.websocket.addEventListener('close', (event) => {
|
||||||
console.log("websocket close")
|
|
||||||
|
|
||||||
this.disconnectAll()
|
this.disconnectAll()
|
||||||
this.finalizeIfAllConnectionsClosed()
|
this.finalizeIfAllConnectionsClosed()
|
||||||
})
|
})
|
||||||
@ -661,7 +665,7 @@ class EngineConnection extends EventTarget {
|
|||||||
type: DisconnectingType.Error,
|
type: DisconnectingType.Error,
|
||||||
value: {
|
value: {
|
||||||
error: ConnectionError.WebSocketError,
|
error: ConnectionError.WebSocketError,
|
||||||
context: event.toString(),
|
context: event,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -675,8 +679,6 @@ class EngineConnection extends EventTarget {
|
|||||||
// when assuming we're the only consumer or that all messages will
|
// when assuming we're the only consumer or that all messages will
|
||||||
// be carefully formatted here.
|
// be carefully formatted here.
|
||||||
|
|
||||||
console.log("websocket message", event)
|
|
||||||
|
|
||||||
if (typeof event.data !== 'string') {
|
if (typeof event.data !== 'string') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -697,6 +699,7 @@ class EngineConnection extends EventTarget {
|
|||||||
`Error in response to request ${message.request_id}:\n${errorsString}
|
`Error in response to request ${message.request_id}:\n${errorsString}
|
||||||
failed cmd type was ${artifactThatFailed?.commandType}`
|
failed cmd type was ${artifactThatFailed?.commandType}`
|
||||||
)
|
)
|
||||||
|
console.log(artifactThatFailed)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Error from server:\n${errorsString}`)
|
console.error(`Error from server:\n${errorsString}`)
|
||||||
}
|
}
|
||||||
@ -802,10 +805,7 @@ failed cmd type was ${artifactThatFailed?.commandType}`
|
|||||||
return this.pc?.setLocalDescription(offer).then(() => {
|
return this.pc?.setLocalDescription(offer).then(() => {
|
||||||
this.send({
|
this.send({
|
||||||
type: 'sdp_offer',
|
type: 'sdp_offer',
|
||||||
offer: {
|
offer,
|
||||||
sdp: offer.sdp || '',
|
|
||||||
type: offer.type,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
this.state = {
|
this.state = {
|
||||||
type: EngineConnectionStateType.Connecting,
|
type: EngineConnectionStateType.Connecting,
|
||||||
@ -824,7 +824,7 @@ failed cmd type was ${artifactThatFailed?.commandType}`
|
|||||||
type: DisconnectingType.Error,
|
type: DisconnectingType.Error,
|
||||||
value: {
|
value: {
|
||||||
error: ConnectionError.LocalDescriptionInvalid,
|
error: ConnectionError.LocalDescriptionInvalid,
|
||||||
context: err.toString(),
|
context: err,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -885,12 +885,52 @@ failed cmd type was ${artifactThatFailed?.commandType}`
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.dispatchEvent(
|
// api-deux currently doesn't report if an auth token is invalid on the
|
||||||
new CustomEvent(EngineConnectionEvents.ConnectionStarted, {
|
// websocket. As a workaround we can invoke two endpoints: /user and
|
||||||
detail: this,
|
// /user/session/{token} . Former for regular operations, latter for
|
||||||
|
// development.
|
||||||
|
// Resolver: https://github.com/KittyCAD/api-deux/issues/1628
|
||||||
|
const promiseIsAuthed = this.token
|
||||||
|
// We can't check tokens in localStorage, at least not yet.
|
||||||
|
// Resolver: https://github.com/KittyCAD/api-deux/issues/1629
|
||||||
|
? Promise.resolve({ status: 200 })
|
||||||
|
: !isTauri()
|
||||||
|
? fetch(withBaseURL('/user'))
|
||||||
|
: invoke<Models['User_type'] | Record<'error_code', unknown>>('get_user', {
|
||||||
|
token: this.token,
|
||||||
|
hostname: VITE_KC_API_BASE_URL,
|
||||||
|
})
|
||||||
|
|
||||||
|
promiseIsAuthed
|
||||||
|
.then((e) => {
|
||||||
|
if (e.status >= 200 && e.status < 400) {
|
||||||
|
createWebSocketConnection()
|
||||||
|
} else if (e.status === 401) {
|
||||||
|
this.state = {
|
||||||
|
type: EngineConnectionStateType.Disconnected,
|
||||||
|
value: {
|
||||||
|
type: DisconnectingType.Error,
|
||||||
|
value: {
|
||||||
|
error: ConnectionError.BadAuthToken,
|
||||||
|
context: e,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.state = {
|
||||||
|
type: EngineConnectionStateType.Disconnected,
|
||||||
|
value: {
|
||||||
|
type: DisconnectingType.Error,
|
||||||
|
value: {
|
||||||
|
error: ConnectionError.Unknown,
|
||||||
|
context: e,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// Do not change this back to an object or any, we should only be sending the
|
// Do not change this back to an object or any, we should only be sending the
|
||||||
// WebSocketRequest type!
|
// WebSocketRequest type!
|
||||||
|
Reference in New Issue
Block a user