feat: implement JWT authentication and token management in frontend and backend

This commit is contained in:
Siddhant Rai
2025-03-14 17:07:15 +05:30
parent fe02bf9347
commit 7fd377bdbe
17 changed files with 453 additions and 178 deletions

View File

@@ -2,28 +2,34 @@ import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { NavLink, useNavigate } from 'react-router-dom';
import conversationService from './api/services/conversationService';
import userService from './api/services/userService';
import Add from './assets/add.svg';
import openNewChat from './assets/openNewChat.svg';
import Hamburger from './assets/hamburger.svg';
import DocsGPT3 from './assets/cute_docsgpt3.svg';
import Discord from './assets/discord.svg';
import Expand from './assets/expand.svg';
import Github from './assets/github.svg';
import Hamburger from './assets/hamburger.svg';
import openNewChat from './assets/openNewChat.svg';
import SettingGear from './assets/settingGear.svg';
import SpinnerDark from './assets/spinner-dark.svg';
import Spinner from './assets/spinner.svg';
import Twitter from './assets/TwitterX.svg';
import UploadIcon from './assets/upload.svg';
import Help from './components/Help';
import SourceDropdown from './components/SourceDropdown';
import {
handleAbort,
selectQueries,
setConversation,
updateConversationId,
handleAbort,
} from './conversation/conversationSlice';
import ConversationTile from './conversation/ConversationTile';
import { useDarkTheme, useMediaQuery } from './hooks';
import useDefaultDocument from './hooks/useDefaultDocument';
import DeleteConvModal from './modals/DeleteConvModal';
import JWTModal from './modals/JWTModal';
import { ActiveState, Doc } from './models/misc';
import { getConversations, getDocs } from './preferences/preferenceApi';
import {
@@ -31,20 +37,17 @@ import {
selectConversationId,
selectConversations,
selectModalStateDeleteConv,
selectPaginatedDocuments,
selectSelectedDocs,
selectSourceDocs,
selectPaginatedDocuments,
selectToken,
setConversations,
setModalStateDeleteConv,
setPaginatedDocuments,
setSelectedDocs,
setSourceDocs,
setPaginatedDocuments,
} from './preferences/preferenceSlice';
import Spinner from './assets/spinner.svg';
import SpinnerDark from './assets/spinner-dark.svg';
import { selectQueries } from './conversation/conversationSlice';
import Upload from './upload/Upload';
import Help from './components/Help';
interface NavigationProps {
navOpen: boolean;
@@ -53,6 +56,7 @@ interface NavigationProps {
export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const dispatch = useDispatch();
const token = useSelector(selectToken);
const queries = useSelector(selectQueries);
const docs = useSelector(selectSourceDocs);
const selectedDocs = useSelector(selectSelectedDocs);
@@ -70,6 +74,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const [uploadModalState, setUploadModalState] =
useState<ActiveState>('INACTIVE');
const [authKeyModalState, setAuthKeyModalState] =
useState<ActiveState>('INACTIVE');
const navRef = useRef(null);
@@ -86,7 +92,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
async function fetchConversations() {
dispatch(setConversations({ ...conversations, loading: true }));
return await getConversations()
return await getConversations(token)
.then((fetchedConversations) => {
dispatch(setConversations(fetchedConversations));
})
@@ -99,7 +105,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const handleDeleteAllConversations = () => {
setIsDeletingConversation(true);
conversationService
.deleteAll()
.deleteAll(token)
.then(() => {
fetchConversations();
})
@@ -109,7 +115,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const handleDeleteConversation = (id: string) => {
setIsDeletingConversation(true);
conversationService
.delete(id, {})
.delete(id, {}, token)
.then(() => {
fetchConversations();
resetConversation();
@@ -119,9 +125,9 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const handleDeleteClick = (doc: Doc) => {
userService
.deletePath(doc.id ?? '')
.deletePath(doc.id ?? '', token)
.then(() => {
return getDocs();
return getDocs(token);
})
.then((updatedDocs) => {
dispatch(setSourceDocs(updatedDocs));
@@ -145,7 +151,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const handleConversationClick = (index: string) => {
conversationService
.getConversation(index)
.getConversation(index, token)
.then((response) => response.json())
.then((data) => {
navigate('/');
@@ -177,7 +183,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
id: string;
}) {
await conversationService
.update(updatedConversation)
.update(updatedConversation, token)
.then((response) => response.json())
.then((data) => {
if (data) {
@@ -197,6 +203,14 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
useEffect(() => {
setNavOpen(!isMobile);
}, [isMobile]);
useEffect(() => {
const authToken = localStorage.getItem('authToken');
if (!authToken) {
setAuthKeyModalState('ACTIVE');
}
}, []);
useDefaultDocument();
return (
@@ -470,6 +484,10 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
close={() => setUploadModalState('INACTIVE')}
></Upload>
)}
<JWTModal
modalState={authKeyModalState}
setModalState={setAuthKeyModalState}
/>
</>
);
}

View File

@@ -4,14 +4,24 @@ const defaultHeaders = {
'Content-Type': 'application/json',
};
const getHeaders = (token: string | null, customHeaders = {}): HeadersInit => {
return {
...defaultHeaders,
...(token ? { Authorization: `Bearer ${token}` } : {}),
...customHeaders,
};
};
const apiClient = {
get: (url: string, headers = {}, signal?: AbortSignal): Promise<any> =>
get: (
url: string,
token: string | null,
headers = {},
signal?: AbortSignal,
): Promise<any> =>
fetch(`${baseURL}${url}`, {
method: 'GET',
headers: {
...defaultHeaders,
...headers,
},
headers: getHeaders(token, headers),
signal,
}).then((response) => {
return response;
@@ -20,15 +30,13 @@ const apiClient = {
post: (
url: string,
data: any,
token: string | null,
headers = {},
signal?: AbortSignal,
): Promise<any> =>
fetch(`${baseURL}${url}`, {
method: 'POST',
headers: {
...defaultHeaders,
...headers,
},
headers: getHeaders(token, headers),
body: JSON.stringify(data),
signal,
}).then((response) => {
@@ -38,28 +46,28 @@ const apiClient = {
put: (
url: string,
data: any,
token: string | null,
headers = {},
signal?: AbortSignal,
): Promise<any> =>
fetch(`${baseURL}${url}`, {
method: 'PUT',
headers: {
...defaultHeaders,
...headers,
},
headers: getHeaders(token, headers),
body: JSON.stringify(data),
signal,
}).then((response) => {
return response;
}),
delete: (url: string, headers = {}, signal?: AbortSignal): Promise<any> =>
delete: (
url: string,
token: string | null,
headers = {},
signal?: AbortSignal,
): Promise<any> =>
fetch(`${baseURL}${url}`, {
method: 'DELETE',
headers: {
...defaultHeaders,
...headers,
},
headers: getHeaders(token, headers),
signal,
}).then((response) => {
return response;

View File

@@ -2,31 +2,58 @@ import apiClient from '../client';
import endpoints from '../endpoints';
const conversationService = {
answer: (data: any, signal: AbortSignal): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.ANSWER, data, {}, signal),
answerStream: (data: any, signal: AbortSignal): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.ANSWER_STREAMING, data, {}, signal),
search: (data: any): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.SEARCH, data),
feedback: (data: any): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.FEEDBACK, data),
getConversation: (id: string): Promise<any> =>
apiClient.get(endpoints.CONVERSATION.CONVERSATION(id)),
getConversations: (): Promise<any> =>
apiClient.get(endpoints.CONVERSATION.CONVERSATIONS),
shareConversation: (isPromptable: boolean, data: any): Promise<any> =>
answer: (
data: any,
token: string | null,
signal: AbortSignal,
): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.ANSWER, data, token, {}, signal),
answerStream: (
data: any,
token: string | null,
signal: AbortSignal,
): Promise<any> =>
apiClient.post(
endpoints.CONVERSATION.ANSWER_STREAMING,
data,
token,
{},
signal,
),
search: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.SEARCH, data, token, {}),
feedback: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.FEEDBACK, data, token, {}),
getConversation: (id: string, token: string | null): Promise<any> =>
apiClient.get(endpoints.CONVERSATION.CONVERSATION(id), token, {}),
getConversations: (token: string | null): Promise<any> =>
apiClient.get(endpoints.CONVERSATION.CONVERSATIONS, token, {}),
shareConversation: (
isPromptable: boolean,
data: any,
token: string | null,
): Promise<any> =>
apiClient.post(
endpoints.CONVERSATION.SHARE_CONVERSATION(isPromptable),
data,
token,
{},
),
getSharedConversation: (identifier: string): Promise<any> =>
apiClient.get(endpoints.CONVERSATION.SHARED_CONVERSATION(identifier)),
delete: (id: string, data: any): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.DELETE(id), data),
deleteAll: (): Promise<any> =>
apiClient.get(endpoints.CONVERSATION.DELETE_ALL),
update: (data: any): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.UPDATE, data),
getSharedConversation: (
identifier: string,
token: string | null,
): Promise<any> =>
apiClient.get(
endpoints.CONVERSATION.SHARED_CONVERSATION(identifier),
token,
{},
),
delete: (id: string, data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.DELETE(id), data, token, {}),
deleteAll: (token: string | null): Promise<any> =>
apiClient.get(endpoints.CONVERSATION.DELETE_ALL, token, {}),
update: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.CONVERSATION.UPDATE, data, token, {}),
};
export default conversationService;

View File

@@ -2,63 +2,71 @@ import apiClient from '../client';
import endpoints from '../endpoints';
const userService = {
getDocs: (): Promise<any> => apiClient.get(`${endpoints.USER.DOCS}`),
getDocsWithPagination: (query: string): Promise<any> =>
apiClient.get(`${endpoints.USER.DOCS_PAGINATED}?${query}`),
checkDocs: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.DOCS_CHECK, data),
getAPIKeys: (): Promise<any> => apiClient.get(endpoints.USER.API_KEYS),
createAPIKey: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.CREATE_API_KEY, data),
deleteAPIKey: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.DELETE_API_KEY, data),
getPrompts: (): Promise<any> => apiClient.get(endpoints.USER.PROMPTS),
createPrompt: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.CREATE_PROMPT, data),
deletePrompt: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.DELETE_PROMPT, data),
updatePrompt: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.UPDATE_PROMPT, data),
getSinglePrompt: (id: string): Promise<any> =>
apiClient.get(endpoints.USER.SINGLE_PROMPT(id)),
deletePath: (docPath: string): Promise<any> =>
apiClient.get(endpoints.USER.DELETE_PATH(docPath)),
getTaskStatus: (task_id: string): Promise<any> =>
apiClient.get(endpoints.USER.TASK_STATUS(task_id)),
getMessageAnalytics: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.MESSAGE_ANALYTICS, data),
getTokenAnalytics: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.TOKEN_ANALYTICS, data),
getFeedbackAnalytics: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.FEEDBACK_ANALYTICS, data),
getLogs: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.LOGS, data),
manageSync: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.MANAGE_SYNC, data),
getAvailableTools: (): Promise<any> =>
apiClient.get(endpoints.USER.GET_AVAILABLE_TOOLS),
getUserTools: (): Promise<any> =>
apiClient.get(endpoints.USER.GET_USER_TOOLS),
createTool: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.CREATE_TOOL, data),
updateToolStatus: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.UPDATE_TOOL_STATUS, data),
updateTool: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.UPDATE_TOOL, data),
deleteTool: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.DELETE_TOOL, data),
getDocs: (token: string | null): Promise<any> =>
apiClient.get(`${endpoints.USER.DOCS}`, token),
getDocsWithPagination: (query: string, token: string | null): Promise<any> =>
apiClient.get(`${endpoints.USER.DOCS_PAGINATED}?${query}`, token),
checkDocs: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.DOCS_CHECK, data, token),
getAPIKeys: (token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.API_KEYS, token),
createAPIKey: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.CREATE_API_KEY, data, token),
deleteAPIKey: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.DELETE_API_KEY, data, token),
getPrompts: (token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.PROMPTS, token),
createPrompt: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.CREATE_PROMPT, data, token),
deletePrompt: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.DELETE_PROMPT, data, token),
updatePrompt: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.UPDATE_PROMPT, data, token),
getSinglePrompt: (id: string, token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.SINGLE_PROMPT(id), token),
deletePath: (docPath: string, token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.DELETE_PATH(docPath), token),
getTaskStatus: (task_id: string, token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.TASK_STATUS(task_id), token),
getMessageAnalytics: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.MESSAGE_ANALYTICS, data, token),
getTokenAnalytics: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.TOKEN_ANALYTICS, data, token),
getFeedbackAnalytics: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.FEEDBACK_ANALYTICS, data, token),
getLogs: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.LOGS, data, token),
manageSync: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.MANAGE_SYNC, data, token),
getAvailableTools: (token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.GET_AVAILABLE_TOOLS, token),
getUserTools: (token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.GET_USER_TOOLS, token),
createTool: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.CREATE_TOOL, data, token),
updateToolStatus: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.UPDATE_TOOL_STATUS, data, token),
updateTool: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.UPDATE_TOOL, data, token),
deleteTool: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.DELETE_TOOL, data, token),
getDocumentChunks: (
docId: string,
page: number,
perPage: number,
token: string | null,
): Promise<any> =>
apiClient.get(endpoints.USER.GET_CHUNKS(docId, page, perPage)),
addChunk: (data: any): Promise<any> =>
apiClient.post(endpoints.USER.ADD_CHUNK, data),
deleteChunk: (docId: string, chunkId: string): Promise<any> =>
apiClient.delete(endpoints.USER.DELETE_CHUNK(docId, chunkId)),
updateChunk: (data: any): Promise<any> =>
apiClient.put(endpoints.USER.UPDATE_CHUNK, data),
apiClient.get(endpoints.USER.GET_CHUNKS(docId, page, perPage), token),
addChunk: (data: any, token: string | null): Promise<any> =>
apiClient.post(endpoints.USER.ADD_CHUNK, data, token),
deleteChunk: (
docId: string,
chunkId: string,
token: string | null,
): Promise<any> =>
apiClient.delete(endpoints.USER.DELETE_CHUNK(docId, chunkId), token),
updateChunk: (data: any, token: string | null): Promise<any> =>
apiClient.put(endpoints.USER.UPDATE_CHUNK, data, token),
};
export default userService;

View File

@@ -15,7 +15,10 @@ import Spinner from '../assets/spinner.svg';
import RetryIcon from '../components/RetryIcon';
import { useDarkTheme, useMediaQuery } from '../hooks';
import { ShareConversationModal } from '../modals/ShareConversationModal';
import { selectConversationId } from '../preferences/preferenceSlice';
import {
selectConversationId,
selectToken,
} from '../preferences/preferenceSlice';
import { AppDispatch } from '../store';
import ConversationBubble from './ConversationBubble';
import { handleSendFeedback } from './conversationHandlers';
@@ -34,6 +37,7 @@ import Upload from '../upload/Upload';
import { ActiveState } from '../models/misc';
export default function Conversation() {
const token = useSelector(selectToken);
const queries = useSelector(selectQueries);
const navigate = useNavigate();
const status = useSelector(selectStatus);
@@ -161,6 +165,7 @@ export default function Conversation() {
feedback,
conversationId as string,
index,
token,
).catch(() =>
handleSendFeedback(
query.prompt,
@@ -168,6 +173,7 @@ export default function Conversation() {
feedback,
conversationId as string,
index,
token,
).catch(() =>
dispatch(updateQuery({ index, query: { feedback: prevFeedback } })),
),

View File

@@ -6,6 +6,7 @@ import { ToolCallsType } from './types';
export function handleFetchAnswer(
question: string,
signal: AbortSignal,
token: string | null,
selectedDocs: Doc | null,
history: Array<any> = [],
conversationId: string | null,
@@ -52,7 +53,7 @@ export function handleFetchAnswer(
}
payload.retriever = selectedDocs?.retriever as string;
return conversationService
.answer(payload, signal)
.answer(payload, token, signal)
.then((response) => {
if (response.ok) {
return response.json();
@@ -76,6 +77,7 @@ export function handleFetchAnswer(
export function handleFetchAnswerSteaming(
question: string,
signal: AbortSignal,
token: string | null,
selectedDocs: Doc | null,
history: Array<any> = [],
conversationId: string | null,
@@ -109,7 +111,7 @@ export function handleFetchAnswerSteaming(
return new Promise<Answer>((resolve, reject) => {
conversationService
.answerStream(payload, signal)
.answerStream(payload, token, signal)
.then((response) => {
if (!response.body) throw Error('No response body');
@@ -160,6 +162,7 @@ export function handleFetchAnswerSteaming(
export function handleSearch(
question: string,
token: string | null,
selectedDocs: Doc | null,
conversation_id: string | null,
history: Array<any> = [],
@@ -185,7 +188,7 @@ export function handleSearch(
payload.active_docs = selectedDocs.id as string;
payload.retriever = selectedDocs?.retriever as string;
return conversationService
.search(payload)
.search(payload, token)
.then((response) => response.json())
.then((data) => {
return data;
@@ -206,11 +209,14 @@ export function handleSearchViaApiKey(
};
});
return conversationService
.search({
question: question,
history: JSON.stringify(history),
api_key: api_key,
})
.search(
{
question: question,
history: JSON.stringify(history),
api_key: api_key,
},
null,
)
.then((response) => response.json())
.then((data) => {
return data;
@@ -224,15 +230,19 @@ export function handleSendFeedback(
feedback: FEEDBACK,
conversation_id: string,
prompt_index: number,
token: string | null,
) {
return conversationService
.feedback({
question: prompt,
answer: response,
feedback: feedback,
conversation_id: conversation_id,
question_index: prompt_index,
})
.feedback(
{
question: prompt,
answer: response,
feedback: feedback,
conversation_id: conversation_id,
question_index: prompt_index,
},
token,
)
.then((response) => {
if (response.ok) {
return Promise.resolve();
@@ -265,7 +275,7 @@ export function handleFetchSharedAnswerStreaming( //for shared conversations
save_conversation: false,
};
conversationService
.answerStream(payload, signal)
.answerStream(payload, null, signal)
.then((response) => {
if (!response.body) throw Error('No response body');
@@ -339,6 +349,7 @@ export function handleFetchSharedAnswer(
question: question,
api_key: apiKey,
},
null,
signal,
)
.then((response) => {

View File

@@ -42,6 +42,7 @@ export const fetchAnswer = createAsyncThunk<
await handleFetchAnswerSteaming(
question,
signal,
state.preference.token,
state.preference.selectedDocs!,
state.conversation.queries,
state.conversation.conversationId,
@@ -53,7 +54,7 @@ export const fetchAnswer = createAsyncThunk<
if (data.type === 'end') {
dispatch(conversationSlice.actions.setStatus('idle'));
getConversations()
getConversations(state.preference.token)
.then((fetchedConversations) => {
dispatch(setConversations(fetchedConversations));
})
@@ -114,6 +115,7 @@ export const fetchAnswer = createAsyncThunk<
const answer = await handleFetchAnswer(
question,
signal,
state.preference.token,
state.preference.selectedDocs!,
state.conversation.queries,
state.conversation.conversationId,
@@ -150,7 +152,7 @@ export const fetchAnswer = createAsyncThunk<
}),
);
dispatch(conversationSlice.actions.setStatus('idle'));
getConversations()
getConversations(state.preference.token)
.then((fetchedConversations) => {
dispatch(setConversations(fetchedConversations));
})

View File

@@ -0,0 +1,54 @@
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import Input from '../components/Input';
import { ActiveState } from '../models/misc';
import { setToken } from '../preferences/preferenceSlice';
import WrapperModal from './WrapperModal';
type JWTModalProps = {
modalState: ActiveState;
setModalState: (state: ActiveState) => void;
};
export default function JWTModal({ modalState, setModalState }: JWTModalProps) {
const dispatch = useDispatch();
const [jwtToken, setJwtToken] = useState<string>('');
const handleSaveToken = () => {
if (jwtToken) {
localStorage.setItem('authToken', jwtToken);
dispatch(setToken(jwtToken));
setModalState('INACTIVE');
}
};
if (modalState !== 'ACTIVE') return null;
return (
<WrapperModal close={() => setModalState('INACTIVE')} className="p-4">
<div className="mb-6">
<span className="text-lg text-jet dark:text-bright-gray">
Add JWT Token
</span>
</div>
<div className="relative mt-5 mb-4">
<Input
type="text"
className="rounded-md"
value={jwtToken}
label="JWT Token"
onChange={(e) => setJwtToken(e.target.value)}
borderVariant="thin"
/>
</div>
<button
disabled={jwtToken.length === 0}
onClick={handleSaveToken}
className="float-right mt-4 rounded-full bg-purple-30 px-5 py-2 text-sm text-white hover:bg-[#6F3FD1] disabled:opacity-50"
>
Save Token
</button>
</WrapperModal>
);
}

View File

@@ -3,9 +3,9 @@ import userService from '../api/services/userService';
import { Doc, GetDocsResponse } from '../models/misc';
//Fetches all JSON objects from the source. We only use the objects with the "model" property in SelectDocsModal.tsx. Hopefully can clean up the source file later.
export async function getDocs(): Promise<Doc[] | null> {
export async function getDocs(token: string | null): Promise<Doc[] | null> {
try {
const response = await userService.getDocs();
const response = await userService.getDocs(token);
const data = await response.json();
const docs: Doc[] = [];
@@ -26,10 +26,11 @@ export async function getDocsWithPagination(
pageNumber = 1,
rowsPerPage = 10,
searchTerm = '',
token: string | null,
): Promise<GetDocsResponse | null> {
try {
const query = `sort=${sort}&order=${order}&page=${pageNumber}&rows=${rowsPerPage}&search=${searchTerm}`;
const response = await userService.getDocsWithPagination(query);
const response = await userService.getDocsWithPagination(query, token);
const data = await response.json();
const docs: Doc[] = [];
Array.isArray(data.paginated) &&
@@ -48,12 +49,12 @@ export async function getDocsWithPagination(
}
}
export async function getConversations(): Promise<{
export async function getConversations(token: string | null): Promise<{
data: { name: string; id: string }[] | null;
loading: boolean;
}> {
try {
const response = await conversationService.getConversations();
const response = await conversationService.getConversations(token);
const data = await response.json();
const conversations: { name: string; id: string }[] = [];
@@ -100,8 +101,11 @@ export function setLocalRecentDocs(doc: Doc | null): void {
docPath = 'local' + '/' + doc.name + '/';
}
userService
.checkDocs({
docs: docPath,
})
.checkDocs(
{
docs: docPath,
},
null,
)
.then((response) => response.json());
}

View File

@@ -19,6 +19,7 @@ export interface Preference {
data: { name: string; id: string }[] | null;
loading: boolean;
};
token: string | null;
modalState: ActiveState;
paginatedDocuments: Doc[] | null;
}
@@ -42,6 +43,7 @@ const initialState: Preference = {
data: null,
loading: false,
},
token: localStorage.getItem('authToken') || null,
modalState: 'INACTIVE',
paginatedDocuments: null,
};
@@ -65,6 +67,9 @@ export const prefSlice = createSlice({
setConversations: (state, action) => {
state.conversations = action.payload;
},
setToken: (state, action) => {
state.token = action.payload;
},
setPrompt: (state, action) => {
state.prompt = action.payload;
},
@@ -85,6 +90,7 @@ export const {
setSelectedDocs,
setSourceDocs,
setConversations,
setToken,
setPrompt,
setChunks,
setTokenLimit,
@@ -157,6 +163,7 @@ export const selectConversations = (state: RootState) =>
state.preference.conversations;
export const selectConversationId = (state: RootState) =>
state.conversation.conversationId;
export const selectToken = (state: RootState) => state.preference.token;
export const selectPrompt = (state: RootState) => state.preference.prompt;
export const selectChunks = (state: RootState) => state.preference.chunks;
export const selectTokenLimit = (state: RootState) =>

View File

@@ -16,6 +16,7 @@ const doc = localStorage.getItem('DocsGPTRecentDocs');
const preloadedState: { preference: Preference } = {
preference: {
apiKey: key ?? '',
token: localStorage.getItem('authToken') ?? null,
prompt:
prompt !== null
? JSON.parse(prompt)