Skip to content

Commit e41bc17

Browse files
author
Jicheng Lu
committed
add global error
1 parent 87dd06d commit e41bc17

File tree

8 files changed

+132
-50
lines changed

8 files changed

+132
-50
lines changed

src/lib/helpers/constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ export const LEARNER_ID = "01acc3e5-0af7-49e6-ad7a-a760bd12dc40";
3535
export const EVALUATOR_ID = "2cd4b805-7078-4405-87e9-2ec9aadf8a11";
3636
export const TRAINING_MODE = "training";
3737

38-
export const DEFAULT_KNOWLEDGE_COLLECTION = "BotSharp";
38+
export const DEFAULT_KNOWLEDGE_COLLECTION = "BotSharp";
39+
export const IMAGE_DATA_PREFIX = 'data:image';

src/lib/helpers/http.js

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import axios from 'axios';
2-
import { getUserStore, loaderStore } from '$lib/helpers/store.js';
2+
import { getUserStore, globalErrorStore, loaderStore } from '$lib/helpers/store.js';
33

44
// Add a request interceptor to attach authentication tokens or headers
55
axios.interceptors.request.use(
66
(config) => {
77
// Add your authentication logic here
8-
let user = getUserStore();
8+
const user = getUserStore();
99
if (!skipLoader(config)) {
1010
loaderStore.set(true);
1111
}
@@ -25,35 +25,48 @@ axios.interceptors.response.use(
2525
(response) => {
2626
// If the request was successful, return the response
2727
loaderStore.set(false);
28+
const user = getUserStore();
29+
30+
const isExpired = Date.now() / 1000 > user.expires;
31+
if (isExpired) {
32+
redirectToLogin();
33+
return Promise.reject('user token expired!');
34+
}
2835
return response;
2936
},
3037
(error) => {
3138
loaderStore.set(false);
32-
let user = getUserStore();
33-
34-
if (Date.now() / 1000 > user.expires) {
35-
error.response = {status: 401};
36-
}
37-
38-
// If the error status is 401, handle it here
39-
if (error.response && error.response.status === 401) {
40-
// Perform actions like redirecting to the login page or refreshing tokens
41-
// Example: redirect to the login page
42-
const curUrl = window.location.pathname + window.location.search;
43-
let loginUrl = 'login';
44-
if (curUrl) {
45-
loginUrl += `?redirect=${encodeURIComponent(curUrl)}`;
46-
}
47-
window.location.href = loginUrl;
39+
const user = getUserStore();
40+
console.log('error', error);
41+
const isExpired = Date.now() / 1000 > user.expires;
42+
if (isExpired || (error.response && error.response.status === 401)) {
43+
redirectToLogin();
44+
return Promise.reject(error);
45+
} else if (!skipGlobalError(error.config)) {
46+
globalErrorStore.set(true);
47+
setTimeout(() => {
48+
globalErrorStore.set(false);
49+
}, 2500);
4850
}
4951

5052
// Return the error to the calling function
5153
return Promise.reject(error);
5254
}
5355
);
5456

57+
58+
function redirectToLogin() {
59+
const curUrl = window.location.pathname + window.location.search;
60+
let loginUrl = 'login';
61+
if (curUrl) {
62+
loginUrl += `?redirect=${encodeURIComponent(curUrl)}`;
63+
}
64+
window.location.href = loginUrl;
65+
}
66+
5567
/** @param {import('axios').InternalAxiosRequestConfig<any>} config */
5668
function skipLoader(config) {
69+
/** @type {RegExp[]} */
5770
const postRegexes = [
5871
new RegExp('http(s*)://(.*?)/conversation/(.*?)/(.*?)', 'g'),
5972
new RegExp('http(s*)://(.*?)/agent', 'g'),
@@ -64,19 +77,22 @@ function skipLoader(config) {
6477
new RegExp('http(s*)://(.*?)/users', 'g')
6578
];
6679

80+
/** @type {RegExp[]} */
6781
const putRegexes = [
6882
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/update', 'g'),
6983
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-message', 'g'),
7084
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-tags', 'g'),
7185
new RegExp('http(s*)://(.*?)/users', 'g'),
7286
];
7387

88+
/** @type {RegExp[]} */
7489
const deleteRegexes = [
7590
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/delete-collection', 'g'),
7691
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data/(.*?)', 'g'),
7792
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data', 'g'),
7893
];
7994

95+
/** @type {RegExp[]} */
8096
const getRegexes = [
8197
new RegExp('http(s*)://(.*?)/setting/(.*?)', 'g'),
8298
new RegExp('http(s*)://(.*?)/user/me', 'g'),
@@ -112,6 +128,56 @@ function skipLoader(config) {
112128
return false;
113129
}
114130

131+
/** @param {import('axios').InternalAxiosRequestConfig<any>} config */
132+
function skipGlobalError(config) {
133+
/** @type {RegExp[]} */
134+
const postRegexes = [
135+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/page', 'g'),
136+
new RegExp('http(s*)://(.*?)/knowledge/(.*?)/search', 'g'),
137+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/create', 'g'),
138+
new RegExp('http(s*)://(.*?)/knowledge/document/(.*?)/page', 'g'),
139+
new RegExp('http(s*)://(.*?)/knowledge/vector/create-collection', 'g'),
140+
new RegExp('http(s*)://(.*?)/refresh-agents', 'g')
141+
];
142+
143+
/** @type {RegExp[]} */
144+
const putRegexes = [
145+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/update', 'g'),
146+
new RegExp('http(s*)://(.*?)/role', 'g'),
147+
new RegExp('http(s*)://(.*?)/user', 'g'),
148+
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-message', 'g'),
149+
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-tags', 'g')
150+
];
151+
152+
/** @type {RegExp[]} */
153+
const deleteRegexes = [
154+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/delete-collection', 'g'),
155+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data/(.*?)', 'g'),
156+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data', 'g'),
157+
];
158+
159+
/** @type {RegExp[]} */
160+
const getRegexes = [];
161+
162+
if (config.method === 'post' && postRegexes.some(regex => regex.test(config.url || ''))) {
163+
return true;
164+
}
165+
166+
if (config.method === 'put' && putRegexes.some(regex => regex.test(config.url || ''))) {
167+
return true;
168+
}
169+
170+
if (config.method === 'delete' && deleteRegexes.some(regex => regex.test(config.url || ''))) {
171+
return true;
172+
}
173+
174+
if (config.method === 'get' && getRegexes.some(regex => regex.test(config.url || ''))) {
175+
return true;
176+
}
177+
178+
return false;
179+
}
180+
115181
/**
116182
* @param {String} url
117183
* @param {Object} args
@@ -120,6 +186,7 @@ function skipLoader(config) {
120186
export function replaceUrl(url, args) {
121187
const keys = Object.keys(args);
122188
keys.forEach(key => {
189+
// @ts-ignore
123190
url = url.replace("{" + key + "}", args[key]);
124191
});
125192
return url;

src/lib/helpers/store.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ const createLoaderStore = () => {
9090
export const loaderStore = createLoaderStore();
9191

9292

93+
const createGlobalErrorStore = () => {
94+
const { subscribe, set } = writable(false);
95+
return {
96+
subscribe,
97+
set
98+
};
99+
}
100+
101+
export const globalErrorStore = createGlobalErrorStore();
102+
103+
93104
const createConversationUserStateStore = () => {
94105
return {
95106
resetAll: () => {

src/routes/+layout.svelte

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,49 @@
22
import { addMessages, init, getLocaleFromNavigator } from 'svelte-i18n';
33
import en from '$lib/langs/en.json';
44
import '$lib/helpers/http';
5-
import { onMount } from 'svelte';
6-
import { loaderStore } from '$lib/helpers/store';
5+
import { onDestroy, onMount } from 'svelte';
6+
import { globalErrorStore, loaderStore } from '$lib/helpers/store';
77
import Loader from '$lib/common/Loader.svelte';
8+
import LoadingToComplete from '$lib/common/LoadingToComplete.svelte';
89
910
addMessages('en', en);
1011
1112
init({
1213
fallbackLocale: 'en',
1314
initialLocale: getLocaleFromNavigator()
1415
});
15-
/**
16-
* @type {boolean}
17-
*/
18-
let isLoading;
16+
17+
/** @type {boolean} */
18+
let isLoading = false;
19+
let hasError = false;
20+
21+
/** @type {any} */
22+
let loaderUnsubscriber;
23+
/** @type {any} */
24+
let errorUnsubscriber;
25+
1926
onMount(() => {
2027
window?.speechSynthesis?.cancel();
21-
const subscribe = loaderStore.subscribe(value => {
28+
loaderUnsubscriber = loaderStore.subscribe(value => {
2229
isLoading = value;
2330
});
31+
32+
errorUnsubscriber = globalErrorStore.subscribe(value => {
33+
hasError = value;
34+
});
2435
})
36+
37+
onDestroy(() => {
38+
loaderUnsubscriber?.();
39+
errorUnsubscriber?.();
40+
});
2541
</script>
2642
{#if isLoading}
2743
<Loader size={50}/>
2844
{/if}
45+
46+
<LoadingToComplete isError={hasError} />
47+
2948
<slot />
3049
3150
<style lang="scss">

src/routes/chat/[agentId]/[conversationId]/chat-box.svelte

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
PUBLIC_LIVECHAT_ENABLE_TRAINING,
4545
PUBLIC_DEBUG_MODE
4646
} from '$env/static/public';
47-
import { BOT_SENDERS, LEARNER_ID, TRAINING_MODE, USER_SENDERS, ADMIN_ROLES } from '$lib/helpers/constants';
47+
import { BOT_SENDERS, LEARNER_ID, TRAINING_MODE, USER_SENDERS, ADMIN_ROLES, IMAGE_DATA_PREFIX } from '$lib/helpers/constants';
4848
import { signalr } from '$lib/services/signalr-service.js';
4949
import { llmRealtime } from '$lib/services/llm-realtime-service.js';
5050
import { newConversation } from '$lib/services/conversation-service';
@@ -92,7 +92,6 @@
9292
const maxTextLength = 64000;
9393
const duration = 2000;
9494
const MESSAGE_STORAGE_KEY = 'message_draft_';
95-
const IMAGE_DATA_PREFIX = 'data:image';
9695
9796
/** @type {import('$agentTypes').AgentModel} */
9897
export let agent;

src/routes/page/agent/[agentId]/+page.svelte

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
/** @type {boolean} */
3737
let isLoading = false;
3838
let isComplete = false;
39-
let isError = false;
4039
4140
const duration = 3000;
4241
const params = $page.params;
@@ -122,10 +121,6 @@
122121
}).catch(err => {
123122
isLoading = false;
124123
isComplete = false;
125-
isError = true;
126-
setTimeout(() => {
127-
isError = false;
128-
}, duration);
129124
});
130125
}
131126
@@ -232,7 +227,7 @@
232227
233228
<HeadTitle title="{$_('Agent Overview')}" />
234229
<Breadcrumb title="{$_('Agent')}" pagetitle="{$_('Agent Overview')}" />
235-
<LoadingToComplete isLoading={isLoading} isComplete={isComplete} isError={isError} />
230+
<LoadingToComplete isLoading={isLoading} isComplete={isComplete} />
236231
237232
{#if agent}
238233
<div>

src/routes/page/conversation/+page.svelte

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
2525
let isLoading = false;
2626
let isComplete = false;
27-
let isError = false;
2827
const duration = 3000;
2928
const firstPage = 1;
3029
const pageSize = 15;
@@ -174,10 +173,6 @@
174173
}).catch(err => {
175174
isLoading = false;
176175
isComplete = false;
177-
isError = true;
178-
setTimeout(() => {
179-
isError = false;
180-
}, duration);
181176
});
182177
}
183178
@@ -353,7 +348,7 @@
353348
354349
<HeadTitle title="{$_('Conversation List')}" />
355350
<Breadcrumb title="{$_('Communication')}" pagetitle="{$_('Conversations')}" />
356-
<LoadingToComplete isLoading={isLoading} isComplete={isComplete} isError={isError} successText={'Delete completed!'} />
351+
<LoadingToComplete isLoading={isLoading} isComplete={isComplete} successText={'Delete completed!'} />
357352
358353
<Row>
359354
<Col lg="12">

src/routes/page/conversation/[conversationId]/conv-dialogs.svelte

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { Card, CardBody, CardTitle, Col, Row } from '@sveltestrap/sveltestrap';
66
import { getDialogs, getConversationFiles, sendNotification } from '$lib/services/conversation-service.js';
77
import { utcToLocal } from '$lib/helpers/datetime';
8-
import { USER_SENDERS } from '$lib/helpers/constants';
8+
import { IMAGE_DATA_PREFIX, USER_SENDERS } from '$lib/helpers/constants';
99
import MessageFileGallery from '$lib/common/MessageFileGallery.svelte';
1010
import { FileSourceType } from '$lib/helpers/enums';
1111
import DialogModal from '$lib/common/DialogModal.svelte';
@@ -21,7 +21,6 @@
2121
/** @type {boolean} */
2222
let isOpenNotificationModal = false;
2323
let isComplete = false;
24-
let isError = false;
2524
2625
/** @type {string} */
2726
let text = '';
@@ -62,19 +61,14 @@
6261
setTimeout(() => {
6362
isComplete = false;
6463
}, duration);
65-
}).catch(() => {
66-
isError = true;
67-
setTimeout(() => {
68-
isError = false;
69-
}, duration);
7064
}).finally(() => {
7165
isOpenNotificationModal = false;
7266
text = '';
7367
});
7468
}
7569
</script>
7670
77-
<LoadingToComplete isComplete={isComplete} isError={isError} successText={'Notification sent!'} />
71+
<LoadingToComplete isComplete={isComplete} successText={'Notification sent!'} />
7872
7973
<DialogModal
8074
title={'Notification'}
@@ -149,9 +143,10 @@
149143
</div>
150144
<div>
151145
<ConvDialogElement dialog={dialog} />
152-
{#if !!dialog.has_message_files}
146+
{#if !!dialog.has_message_files || dialog.data?.startsWith(IMAGE_DATA_PREFIX)}
153147
<MessageFileGallery
154-
messageId={dialog?.message_id}
148+
message={dialog}
149+
appendImage
155150
galleryClasses={'dialog-file-display'}
156151
fetchFiles={() => getConversationFiles(conversation.id, dialog.message_id, showInRight(dialog) ? FileSourceType.User : FileSourceType.Bot)}
157152
/>

0 commit comments

Comments
 (0)