mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
add middleware to handle 403 errors (#5245)
* add middleware to handle 403 errors * remove log * add logic to warn the user if not all requested images could be deleted * lint * fix copy * feat(ui): simplify batchEnqueuedListener error toast logic * feat(ui): use translations for error messages * chore(ui): lint --------- Co-authored-by: Mary Hipp <maryhipp@Marys-MacBook-Air.local> Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
This commit is contained in:
parent
db58efbe65
commit
b519b6e1e0
@ -161,6 +161,7 @@
|
|||||||
"txt2img": "Text To Image",
|
"txt2img": "Text To Image",
|
||||||
"unifiedCanvas": "Unified Canvas",
|
"unifiedCanvas": "Unified Canvas",
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
|
"unknownError": "Unknown Error",
|
||||||
"upload": "Upload"
|
"upload": "Upload"
|
||||||
},
|
},
|
||||||
"controlnet": {
|
"controlnet": {
|
||||||
@ -384,7 +385,9 @@
|
|||||||
"deleteSelection": "Delete Selection",
|
"deleteSelection": "Delete Selection",
|
||||||
"downloadSelection": "Download Selection",
|
"downloadSelection": "Download Selection",
|
||||||
"preparingDownload": "Preparing Download",
|
"preparingDownload": "Preparing Download",
|
||||||
"preparingDownloadFailed": "Problem Preparing Download"
|
"preparingDownloadFailed": "Problem Preparing Download",
|
||||||
|
"problemDeletingImages": "Problem Deleting Images",
|
||||||
|
"problemDeletingImagesDesc": "One or more images could not be deleted"
|
||||||
},
|
},
|
||||||
"hotkeys": {
|
"hotkeys": {
|
||||||
"acceptStagingImage": {
|
"acceptStagingImage": {
|
||||||
|
@ -3,7 +3,7 @@ import { logger } from 'app/logging/logger';
|
|||||||
import { parseify } from 'common/util/serialize';
|
import { parseify } from 'common/util/serialize';
|
||||||
import { zPydanticValidationError } from 'features/system/store/zodSchemas';
|
import { zPydanticValidationError } from 'features/system/store/zodSchemas';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { get, truncate, upperFirst } from 'lodash-es';
|
import { truncate, upperFirst } from 'lodash-es';
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
import { TOAST_OPTIONS, theme } from 'theme/theme';
|
import { TOAST_OPTIONS, theme } from 'theme/theme';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
@ -74,22 +74,11 @@ export const addBatchEnqueuedListener = () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else if (response.status !== 403) {
|
||||||
let detail = 'Unknown Error';
|
|
||||||
let duration = undefined;
|
|
||||||
if (response.status === 403 && 'body' in response) {
|
|
||||||
detail = get(response, 'body.detail', 'Unknown Error');
|
|
||||||
} else if (response.status === 403 && 'error' in response) {
|
|
||||||
detail = get(response, 'error.detail', 'Unknown Error');
|
|
||||||
} else if (response.status === 403 && 'data' in response) {
|
|
||||||
detail = get(response, 'data.detail', 'Unknown Error');
|
|
||||||
duration = 15000;
|
|
||||||
}
|
|
||||||
toast({
|
toast({
|
||||||
title: t('queue.batchFailedToQueue'),
|
title: t('queue.batchFailedToQueue'),
|
||||||
|
description: t('common.unknownError'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
description: detail,
|
|
||||||
...(duration ? { duration } : {}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
logger('queue').error(
|
logger('queue').error(
|
||||||
|
@ -109,20 +109,9 @@ export const addControlNetImageProcessedListener = () => {
|
|||||||
t('queue.graphFailedToQueue')
|
t('queue.graphFailedToQueue')
|
||||||
);
|
);
|
||||||
|
|
||||||
// handle usage-related errors
|
|
||||||
if (error instanceof Object) {
|
if (error instanceof Object) {
|
||||||
if ('data' in error && 'status' in error) {
|
if ('data' in error && 'status' in error) {
|
||||||
if (error.status === 403) {
|
if (error.status === 403) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const detail = (error.data as any)?.detail || 'Unknown Error';
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.graphFailedToQueue'),
|
|
||||||
status: 'error',
|
|
||||||
description: detail,
|
|
||||||
duration: 15000,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
dispatch(pendingControlImagesCleared());
|
dispatch(pendingControlImagesCleared());
|
||||||
dispatch(controlAdapterImageChanged({ id, controlImage: null }));
|
dispatch(controlAdapterImageChanged({ id, controlImage: null }));
|
||||||
return;
|
return;
|
||||||
|
@ -75,31 +75,20 @@ export const addUpscaleRequestedListener = () => {
|
|||||||
t('queue.graphFailedToQueue')
|
t('queue.graphFailedToQueue')
|
||||||
);
|
);
|
||||||
|
|
||||||
// handle usage-related errors
|
if (
|
||||||
if (error instanceof Object) {
|
error instanceof Object &&
|
||||||
if ('data' in error && 'status' in error) {
|
'status' in error &&
|
||||||
if (error.status === 403) {
|
error.status === 403
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
) {
|
||||||
const detail = (error.data as any)?.detail || 'Unknown Error';
|
return;
|
||||||
dispatch(
|
} else {
|
||||||
addToast({
|
dispatch(
|
||||||
title: t('queue.graphFailedToQueue'),
|
addToast({
|
||||||
status: 'error',
|
title: t('queue.graphFailedToQueue'),
|
||||||
description: detail,
|
status: 'error',
|
||||||
duration: 15000,
|
})
|
||||||
})
|
);
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.graphFailedToQueue'),
|
|
||||||
status: 'error',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -33,6 +33,7 @@ import { actionsDenylist } from './middleware/devtools/actionsDenylist';
|
|||||||
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
|
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
|
||||||
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||||
import { createStore as createIDBKeyValStore, get, set } from 'idb-keyval';
|
import { createStore as createIDBKeyValStore, get, set } from 'idb-keyval';
|
||||||
|
import { authToastMiddleware } from 'services/api/authToastMiddleware';
|
||||||
|
|
||||||
const allReducers = {
|
const allReducers = {
|
||||||
canvas: canvasReducer,
|
canvas: canvasReducer,
|
||||||
@ -107,6 +108,7 @@ export const createStore = (uniqueStoreKey?: string) =>
|
|||||||
})
|
})
|
||||||
.concat(api.middleware)
|
.concat(api.middleware)
|
||||||
.concat(dynamicMiddlewares)
|
.concat(dynamicMiddlewares)
|
||||||
|
.concat(authToastMiddleware)
|
||||||
.prepend(listenerMiddleware.middleware),
|
.prepend(listenerMiddleware.middleware),
|
||||||
devTools: {
|
devTools: {
|
||||||
actionSanitizer,
|
actionSanitizer,
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
import { isRejectedWithValue } from '@reduxjs/toolkit';
|
||||||
|
import type { MiddlewareAPI, Middleware } from '@reduxjs/toolkit';
|
||||||
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
|
export const authToastMiddleware: Middleware =
|
||||||
|
(api: MiddlewareAPI) => (next) => (action) => {
|
||||||
|
if (isRejectedWithValue(action)) {
|
||||||
|
if (action.payload.status === 403) {
|
||||||
|
const { dispatch } = api;
|
||||||
|
const customMessage =
|
||||||
|
action.payload.data.detail !== 'Forbidden'
|
||||||
|
? action.payload.data.detail
|
||||||
|
: undefined;
|
||||||
|
dispatch(
|
||||||
|
addToast({
|
||||||
|
title: t('common.somethingWentWrong'),
|
||||||
|
status: 'error',
|
||||||
|
description: customMessage,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
};
|
@ -27,6 +27,8 @@ import {
|
|||||||
imagesSelectors,
|
imagesSelectors,
|
||||||
} from 'services/api/util';
|
} from 'services/api/util';
|
||||||
import { boardsApi } from './boards';
|
import { boardsApi } from './boards';
|
||||||
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const imagesApi = api.injectEndpoints({
|
export const imagesApi = api.injectEndpoints({
|
||||||
endpoints: (build) => ({
|
endpoints: (build) => ({
|
||||||
@ -208,6 +210,16 @@ export const imagesApi = api.injectEndpoints({
|
|||||||
try {
|
try {
|
||||||
const { data } = await queryFulfilled;
|
const { data } = await queryFulfilled;
|
||||||
|
|
||||||
|
if (data.deleted_images.length < imageDTOs.length) {
|
||||||
|
dispatch(
|
||||||
|
addToast({
|
||||||
|
title: t('gallery.problemDeletingImages'),
|
||||||
|
description: t('gallery.problemDeletingImagesDesc'),
|
||||||
|
status: 'warning',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// convert to an object so we can access the successfully delete image DTOs by name
|
// convert to an object so we can access the successfully delete image DTOs by name
|
||||||
const groupedImageDTOs = keyBy(imageDTOs, 'image_name');
|
const groupedImageDTOs = keyBy(imageDTOs, 'image_name');
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user