Dashboard and error handling
This commit is contained in:
31
packages/sshecret-frontend/src/api/assertSdkResponseOk.ts
Normal file
31
packages/sshecret-frontend/src/api/assertSdkResponseOk.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// /src/utils/assertSdkResponseOk.ts
|
||||
import type { AxiosResponse, AxiosError } from 'axios'
|
||||
import { ApiError, ValidationError, NotFoundError } from '@/api/errors'
|
||||
import { isHttpValidationError } from './typeguards';
|
||||
|
||||
export type SDKResponse<T> =
|
||||
| (AxiosResponse<T> & { error: undefined })
|
||||
| (AxiosError<any> & { data: undefined; error: any })
|
||||
|
||||
export function assertSdkResponseOk<T>(response: SDKResponse<T>): T {
|
||||
if ('error' in response && response.error) {
|
||||
const status = response.status ?? 500
|
||||
|
||||
if (status === 404) {
|
||||
throw new NotFoundError("Not found")
|
||||
}
|
||||
|
||||
if (status === 422 && isHttpValidationError(response.error)) {
|
||||
throw new ValidationError(response.error.detail ?? [])
|
||||
}
|
||||
|
||||
const errorMessage = response.error.detail ?? 'Unknown error'
|
||||
throw new ApiError(`API error: ${errorMessage}`, status)
|
||||
}
|
||||
|
||||
if ('data' in response && response.data) {
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new ApiError('Invalid response shape', 500)
|
||||
}
|
||||
28
packages/sshecret-frontend/src/api/errors.ts
Normal file
28
packages/sshecret-frontend/src/api/errors.ts
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
export class ApiError extends Error {
|
||||
public status: number
|
||||
|
||||
constructor(message: string, status: number) {
|
||||
super(message)
|
||||
this.name = 'ApiError'
|
||||
this.status = status
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends ApiError {
|
||||
constructor(message: string) {
|
||||
super(message, 404)
|
||||
this.name = "NotFoundError"
|
||||
}
|
||||
}
|
||||
|
||||
export class ValidationError extends ApiError {
|
||||
public errors: any[]
|
||||
|
||||
constructor(errors: any[], message: string = 'Validation failed') {
|
||||
super(message, 422)
|
||||
this.name = 'ValidationError'
|
||||
this.errors = errors
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { client } from '@/client/client.gen'
|
||||
import { useAuthTokenStore } from '@/store/auth.ts'
|
||||
|
||||
import router from '@/router'
|
||||
|
||||
|
||||
client.instance.interceptors.response.use(
|
||||
response => response,
|
||||
@ -11,6 +13,7 @@ client.instance.interceptors.response.use(
|
||||
if (originalRequest.url.includes("/refresh")) {
|
||||
const auth = useAuthTokenStore()
|
||||
auth.logout()
|
||||
router.push({ name: 'login' })
|
||||
return Promise.reject("Refresh failed - logged out")
|
||||
}
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
@ -27,6 +30,6 @@ client.instance.interceptors.response.use(
|
||||
}
|
||||
return Promise.reject("Could not refresh token")
|
||||
}
|
||||
return Promise.reject("Could not refresh token")
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import { OPERATIONS } from '@/api/types'
|
||||
import { SUBSYSTEM } from '@/api/types'
|
||||
import type { Operation, SubSystem } from '@/client'
|
||||
import type { HttpValidationError, Operation, SubSystem } from '@/client'
|
||||
|
||||
export function isOperation(value: string): value is Operation {
|
||||
return (OPERATIONS as readonly string[]).includes(value)
|
||||
@ -11,3 +11,7 @@ export function isOperation(value: string): value is Operation {
|
||||
export function isSubSystem(value: string): value is SubSystem {
|
||||
return (SUBSYSTEM as readonly string[]).includes(value)
|
||||
}
|
||||
|
||||
export function isHttpValidationError(error: unknown): error is HttpValidationError {
|
||||
return !!(error && typeof error === 'object' && 'detail' in error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user