diff --git a/src/lib/common/FileGallery.svelte b/src/lib/common/FileGallery.svelte
index cc081daa..a800baaa 100644
--- a/src/lib/common/FileGallery.svelte
+++ b/src/lib/common/FileGallery.svelte
@@ -103,7 +103,7 @@
{/if}
- {#if needDownload}
+ {#if needDownload && !!file.file_download_url}
Promise
} */
export let fetchFiles = () => Promise.resolve([]);
@@ -25,31 +28,38 @@
/** @type {import('$fileTypes').AudioFileModel[]} */
let audioFiles = [];
- onMount(() => {
+ onMount(async () => {
if (fetchFiles != null && fetchFiles != undefined) {
- fetchFiles().then(data => {
- // @ts-ignore
- const validFiles = data?.filter(item => !!item.file_url) || [];
- // @ts-ignore
- textFiles = validFiles.filter(item => !isAudio(item.file_extension)).map(item => {
- return {
- file_name: item.file_name,
- file_extension: item.file_extension,
- file_data: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`,
- file_download_url: isExternalUrl(item.file_download_url) ? item.file_download_url : `${PUBLIC_SERVICE_URL}${item.file_download_url}?access_token=${$userStore?.token}`
- };
- });
- // @ts-ignore
- audioFiles = validFiles.filter(item => isAudio(item.file_extension)).map(item => {
- return {
- name: item.file_name,
- cover: AUDIO_ICON,
- artist: '',
- url: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`
- };
- });
+ const res = await fetchFiles();
+ // @ts-ignore
+ const validFiles = res?.filter(item => !!item.file_url) || [];
+ // @ts-ignore
+ textFiles = validFiles.filter(item => !isAudio(item.file_extension)).map(item => {
+ return {
+ file_name: item.file_name,
+ file_extension: item.file_extension,
+ file_data: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`,
+ file_download_url: isExternalUrl(item.file_download_url) ? item.file_download_url : `${PUBLIC_SERVICE_URL}${item.file_download_url}?access_token=${$userStore?.token}`
+ };
+ });
+ // @ts-ignore
+ audioFiles = validFiles.filter(item => isAudio(item.file_extension)).map(item => {
+ return {
+ name: item.file_name,
+ cover: AUDIO_ICON,
+ artist: '',
+ url: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`
+ };
});
}
+
+ if (appendImage && message?.data) {
+ textFiles = [...textFiles, {
+ file_name: 'data',
+ file_extension: '',
+ file_data: message?.data
+ }];
+ }
});
/** @param {number} idx */
@@ -71,7 +81,7 @@
onDownload={idx => handleDownloadFile(idx)}
/>
{
// Add your authentication logic here
- let user = getUserStore();
+ const user = getUserStore();
if (!skipLoader(config)) {
loaderStore.set(true);
}
@@ -25,26 +25,28 @@ axios.interceptors.response.use(
(response) => {
// If the request was successful, return the response
loaderStore.set(false);
+ const user = getUserStore();
+
+ const isExpired = Date.now() / 1000 > user.expires;
+ if (isExpired) {
+ redirectToLogin();
+ return Promise.reject('user token expired!');
+ }
return response;
},
(error) => {
loaderStore.set(false);
- let user = getUserStore();
-
- if (Date.now() / 1000 > user.expires) {
- error.response = {status: 401};
- }
-
- // If the error status is 401, handle it here
- if (error.response && error.response.status === 401) {
- // Perform actions like redirecting to the login page or refreshing tokens
- // Example: redirect to the login page
- const curUrl = window.location.pathname + window.location.search;
- let loginUrl = 'login';
- if (curUrl) {
- loginUrl += `?redirect=${encodeURIComponent(curUrl)}`;
- }
- window.location.href = loginUrl;
+ const user = getUserStore();
+ console.log('error', error);
+ const isExpired = Date.now() / 1000 > user.expires;
+ if (isExpired || (error.response && error.response.status === 401)) {
+ redirectToLogin();
+ return Promise.reject(error);
+ } else if (!skipGlobalError(error.config)) {
+ globalErrorStore.set(true);
+ setTimeout(() => {
+ globalErrorStore.set(false);
+ }, 2500);
}
// Return the error to the calling function
@@ -52,8 +54,19 @@ axios.interceptors.response.use(
}
);
+
+function redirectToLogin() {
+ const curUrl = window.location.pathname + window.location.search;
+ let loginUrl = 'login';
+ if (curUrl) {
+ loginUrl += `?redirect=${encodeURIComponent(curUrl)}`;
+ }
+ window.location.href = loginUrl;
+}
+
/** @param {import('axios').InternalAxiosRequestConfig} config */
function skipLoader(config) {
+ /** @type {RegExp[]} */
const postRegexes = [
new RegExp('http(s*)://(.*?)/conversation/(.*?)/(.*?)', 'g'),
new RegExp('http(s*)://(.*?)/agent', 'g'),
@@ -64,6 +77,7 @@ function skipLoader(config) {
new RegExp('http(s*)://(.*?)/users', 'g')
];
+ /** @type {RegExp[]} */
const putRegexes = [
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/update', 'g'),
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-message', 'g'),
@@ -71,12 +85,14 @@ function skipLoader(config) {
new RegExp('http(s*)://(.*?)/users', 'g'),
];
+ /** @type {RegExp[]} */
const deleteRegexes = [
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/delete-collection', 'g'),
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data/(.*?)', 'g'),
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data', 'g'),
];
+ /** @type {RegExp[]} */
const getRegexes = [
new RegExp('http(s*)://(.*?)/setting/(.*?)', 'g'),
new RegExp('http(s*)://(.*?)/user/me', 'g'),
@@ -112,6 +128,56 @@ function skipLoader(config) {
return false;
}
+/** @param {import('axios').InternalAxiosRequestConfig} config */
+function skipGlobalError(config) {
+ /** @type {RegExp[]} */
+ const postRegexes = [
+ new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/page', 'g'),
+ new RegExp('http(s*)://(.*?)/knowledge/(.*?)/search', 'g'),
+ new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/create', 'g'),
+ new RegExp('http(s*)://(.*?)/knowledge/document/(.*?)/page', 'g'),
+ new RegExp('http(s*)://(.*?)/knowledge/vector/create-collection', 'g'),
+ new RegExp('http(s*)://(.*?)/refresh-agents', 'g')
+ ];
+
+ /** @type {RegExp[]} */
+ const putRegexes = [
+ new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/update', 'g'),
+ new RegExp('http(s*)://(.*?)/role', 'g'),
+ new RegExp('http(s*)://(.*?)/user', 'g'),
+ new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-message', 'g'),
+ new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-tags', 'g')
+ ];
+
+ /** @type {RegExp[]} */
+ const deleteRegexes = [
+ new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/delete-collection', 'g'),
+ new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data/(.*?)', 'g'),
+ new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data', 'g'),
+ ];
+
+ /** @type {RegExp[]} */
+ const getRegexes = [];
+
+ if (config.method === 'post' && postRegexes.some(regex => regex.test(config.url || ''))) {
+ return true;
+ }
+
+ if (config.method === 'put' && putRegexes.some(regex => regex.test(config.url || ''))) {
+ return true;
+ }
+
+ if (config.method === 'delete' && deleteRegexes.some(regex => regex.test(config.url || ''))) {
+ return true;
+ }
+
+ if (config.method === 'get' && getRegexes.some(regex => regex.test(config.url || ''))) {
+ return true;
+ }
+
+ return false;
+}
+
/**
* @param {String} url
* @param {Object} args
@@ -120,6 +186,7 @@ function skipLoader(config) {
export function replaceUrl(url, args) {
const keys = Object.keys(args);
keys.forEach(key => {
+ // @ts-ignore
url = url.replace("{" + key + "}", args[key]);
});
return url;
diff --git a/src/lib/helpers/store.js b/src/lib/helpers/store.js
index 328cbb49..4d886565 100644
--- a/src/lib/helpers/store.js
+++ b/src/lib/helpers/store.js
@@ -90,6 +90,17 @@ const createLoaderStore = () => {
export const loaderStore = createLoaderStore();
+const createGlobalErrorStore = () => {
+ const { subscribe, set } = writable(false);
+ return {
+ subscribe,
+ set
+ };
+}
+
+export const globalErrorStore = createGlobalErrorStore();
+
+
const createConversationUserStateStore = () => {
return {
resetAll: () => {
diff --git a/src/lib/helpers/types/conversationTypes.js b/src/lib/helpers/types/conversationTypes.js
index 6bce4176..070b7b2a 100644
--- a/src/lib/helpers/types/conversationTypes.js
+++ b/src/lib/helpers/types/conversationTypes.js
@@ -41,7 +41,8 @@
* @typedef {Object} StateSearchQuery
* @property {string} query - The search query.
* @property {number?} [keyLimit] - The key limit.
- * @property {boolean?} [preLoad] - Whether it is preloading or not.
+ * @property {number?} [convLimit] - The conversation limit.
+ * @property {boolean?} [preload] - Whether it is preloading or not.
*/
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 678b4039..13c71ce3 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -2,9 +2,10 @@
import { addMessages, init, getLocaleFromNavigator } from 'svelte-i18n';
import en from '$lib/langs/en.json';
import '$lib/helpers/http';
- import { onMount } from 'svelte';
- import { loaderStore } from '$lib/helpers/store';
+ import { onDestroy, onMount } from 'svelte';
+ import { globalErrorStore, loaderStore } from '$lib/helpers/store';
import Loader from '$lib/common/Loader.svelte';
+ import LoadingToComplete from '$lib/common/LoadingToComplete.svelte';
addMessages('en', en);
@@ -12,20 +13,38 @@
fallbackLocale: 'en',
initialLocale: getLocaleFromNavigator()
});
- /**
- * @type {boolean}
- */
- let isLoading;
+
+ /** @type {boolean} */
+ let isLoading = false;
+ let hasError = false;
+
+ /** @type {any} */
+ let loaderUnsubscriber;
+ /** @type {any} */
+ let errorUnsubscriber;
+
onMount(() => {
window?.speechSynthesis?.cancel();
- const subscribe = loaderStore.subscribe(value => {
+ loaderUnsubscriber = loaderStore.subscribe(value => {
isLoading = value;
});
+
+ errorUnsubscriber = globalErrorStore.subscribe(value => {
+ hasError = value;
+ });
})
+
+ onDestroy(() => {
+ loaderUnsubscriber?.();
+ errorUnsubscriber?.();
+ });
{#if isLoading}
{/if}
+
+
+