From 1a8da59a63ef2e214154afd301a33ea984e1a25f Mon Sep 17 00:00:00 2001 From: Swapnil Tripathi Date: Wed, 7 Aug 2024 10:40:44 +0530 Subject: [PATCH] fix server and client communication Signed-off-by: Swapnil Tripathi --- src/core/.gitignore | 162 ++++++++++++++++++++ src/core/api.py | 39 ++--- src/core/api_router.py | 58 ------- src/core/{main.py => conversation.py} | 3 +- src/core/main_router.py | 8 - src/core/routes/__init__.py | 0 src/core/{rag_router.py => routes/main.py} | 10 +- src/core/routes/test.py | 54 +++++++ src/core/utils.py | 2 +- src/frontend/app/page.tsx | 45 +----- src/frontend/components/chat-bottom-bar.tsx | 111 ++++++++------ src/frontend/components/chat-page.tsx | 29 ++++ src/frontend/components/chat-request.tsx | 4 +- src/frontend/components/chat-response.tsx | 2 +- src/frontend/components/chat-section.tsx | 27 ++-- src/frontend/lib/apis/fetcher.ts | 16 ++ src/frontend/lib/const.ts | 1 + src/frontend/lib/types.ts | 6 + src/frontend/next.config.mjs | 13 +- src/frontend/package.json | 2 + src/frontend/yarn.lock | 10 ++ 21 files changed, 396 insertions(+), 206 deletions(-) create mode 100644 src/core/.gitignore delete mode 100644 src/core/api_router.py rename src/core/{main.py => conversation.py} (99%) delete mode 100644 src/core/main_router.py create mode 100644 src/core/routes/__init__.py rename src/core/{rag_router.py => routes/main.py} (85%) create mode 100644 src/core/routes/test.py create mode 100644 src/frontend/components/chat-page.tsx create mode 100644 src/frontend/lib/apis/fetcher.ts create mode 100644 src/frontend/lib/const.ts create mode 100644 src/frontend/lib/types.ts diff --git a/src/core/.gitignore b/src/core/.gitignore new file mode 100644 index 0000000..82f9275 --- /dev/null +++ b/src/core/.gitignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/src/core/api.py b/src/core/api.py index 70c22f8..ee11e13 100644 --- a/src/core/api.py +++ b/src/core/api.py @@ -1,28 +1,29 @@ +import os +import uvicorn from utils import load_yaml_file -from main_router import main_router -from rag_router import rag_router -from api_router import api_router from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -import uvicorn +# from routes.main import router as main_router +from routes.test import router as test_router config_data = load_yaml_file("config.yaml") - -app = FastAPI() -# Enable CORS -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], # Allows all origins - allow_credentials=True, - allow_methods=["GET", "POST", "OPTIONS"], - allow_headers=["*"], -) +def create_app() -> FastAPI: + # Create the FastAPI application + app = FastAPI() + + # Enable CORS + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Allows all origins + allow_credentials=True, + allow_methods=["GET", "POST", "OPTIONS"], + allow_headers=["*"], + ) + # app.include_router(main_router) + app.include_router(test_router) -# Include routers -app.include_router(main_router) -api_router.include_router(rag_router) -app.include_router(api_router,prefix="/api") + return app -uvicorn.run(app,host=config_data["host"],port=config_data["port"]) \ No newline at end of file +uvicorn.run(create_app,host=config_data["host"],port=config_data["port"]) \ No newline at end of file diff --git a/src/core/api_router.py b/src/core/api_router.py deleted file mode 100644 index 019f7c6..0000000 --- a/src/core/api_router.py +++ /dev/null @@ -1,58 +0,0 @@ -from fastapi import APIRouter, HTTPException -from pydantic import BaseModel - - - -#Conversation Request class that contains the question to be prompt -class ConversationRequest(BaseModel): - question: str -#ConversationResponse class that contains the answer of the prompt -class ConversationResponse(BaseModel): - answer: str - -#Initialization of the router : -api_router = APIRouter() - - -# Responses of the questions : -responses = { - "what_is_hyperledger_fabric": { - "answer": "Hyperledger Fabric is an open-source blockchain framework designed for enterprise solutions. It allows for a modular architecture where different components like consensus mechanisms, membership services, and ledger storage can be tailored to specific needs." - }, - "how_to_install_hyperledger_fabric": { - "answer": "To install Hyperledger Fabric, follow these steps:\n" - "1. Install Docker and Docker Compose.\n" - "2. Download the Hyperledger Fabric binaries and docker images using the Fabric CA and Fabric binaries script.\n" - "3. Set up the environment variables for Fabric binaries.\n" - "4. Verify the installation by running Fabric sample network scripts." - }, - "how_to_deploy": { - "answer": "To deploy a Hyperledger Fabric network:\n" - "1. Define the network topology and configuration files (e.g., 'configtx.yaml').\n" - "2. Use the 'fabric-cli' or scripts to create channel artifacts.\n" - "3. Launch the network by starting the necessary Docker containers and services using 'docker-compose' or Kubernetes.\n" - "4. Instantiate and upgrade chaincode as needed." - }, - "how_to_run": { - "answer": "To run a Hyperledger Fabric network:\n" - "1. Start the network by running 'docker-compose up' or the appropriate command for your setup.\n" - "2. Use the Fabric CLI tools or SDKs to interact with the network, including creating and joining channels, and submitting transactions.\n" - "3. Monitor the network's health and performance using Fabric's built-in tools or external monitoring solutions." - }, - "how_to_ensure_data_privacy": { - "answer": "To ensure data privacy in Hyperledger Fabric:\n" - "1. Use private data collections to restrict access to sensitive data.\n" - "2. Implement access control policies and endorsement policies.\n" - "3. Utilize encryption for data at rest and in transit.\n" - "4. Regularly review and update security configurations and practices." - } -} - -#post route where the user can ask questions and get response -@api_router.post("/conversation", response_model=ConversationResponse) -def post_conversation(item: ConversationRequest) -> ConversationResponse: - question_key = item.question.lower().replace(" ", "_").replace("?", "") - if question_key in responses: - return ConversationResponse(answer=responses[question_key]['answer']) - else: - raise HTTPException(status_code=404, detail="Question not found. Please check the question and try again.") \ No newline at end of file diff --git a/src/core/main.py b/src/core/conversation.py similarity index 99% rename from src/core/main.py rename to src/core/conversation.py index b7600c7..9a3b36b 100644 --- a/src/core/main.py +++ b/src/core/conversation.py @@ -1,5 +1,4 @@ import torch -from utils import load_yaml_file from transformers import pipeline from langchain_huggingface import HuggingFacePipeline from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder @@ -11,7 +10,7 @@ from tokenizer import initialize_tokenizer from embeddings import embedding_function from session_history import get_session_history - +from utils import load_yaml_file def get_conversation(): config_data = load_yaml_file("config.yaml") diff --git a/src/core/main_router.py b/src/core/main_router.py deleted file mode 100644 index 3616716..0000000 --- a/src/core/main_router.py +++ /dev/null @@ -1,8 +0,0 @@ -from fastapi import APIRouter - -main_router = APIRouter() - -# reply to GET requests, if the service is running -@main_router.get("/") -def hello(): - return {"msg": "hello"} \ No newline at end of file diff --git a/src/core/routes/__init__.py b/src/core/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/core/rag_router.py b/src/core/routes/main.py similarity index 85% rename from src/core/rag_router.py rename to src/core/routes/main.py index 84ae836..abfe0fd 100644 --- a/src/core/rag_router.py +++ b/src/core/routes/main.py @@ -1,22 +1,18 @@ from fastapi import APIRouter, HTTPException from pydantic import BaseModel -from main import get_conversation - +from conversation import get_conversation conversational_rag_chain = get_conversation() - # define the Query class that contains the question class Query(BaseModel): text: str - #Initialization of the router : -rag_router = APIRouter() - +router = APIRouter() # reply to POST requests: '{"text": "How to install Hyperledger fabric?"}' -@rag_router.post("/query") +@router.post("/query") def answer(q: Query): question = q.text ai_msg_1 = conversational_rag_chain.invoke( diff --git a/src/core/routes/test.py b/src/core/routes/test.py new file mode 100644 index 0000000..6c8a770 --- /dev/null +++ b/src/core/routes/test.py @@ -0,0 +1,54 @@ +from typing import List +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel +import uuid + +router = APIRouter( + prefix="/api/test" +) + +class ResponseMessage(BaseModel): + id: str + content: str + type: int + +class ResponseConversation(BaseModel): + id: str + message: ResponseMessage + +class RequestConversation(BaseModel): + id: str + content: str + type: int + +responses = { + "What is Hyperledger Fabric?": "Hyperledger Fabric is an open-source blockchain framework designed for enterprise solutions. It allows for a modular architecture where different components like consensus mechanisms, membership services, and ledger storage can be tailored to specific needs.", + "How to install Hyperledger Fabric?": "To install Hyperledger Fabric, follow these steps:\n1. Install Docker and Docker Compose.\n2. Download the Hyperledger Fabric binaries and docker images using the Fabric CA and Fabric binaries script.\n3. Set up the environment variables for Fabric binaries.\n4. Verify the installation by running Fabric sample network scripts.", + "How to deploy a Hyperledger Fabric network?": "To deploy a Hyperledger Fabric network:\n1. Define the network topology and configuration files (e.g., 'configtx.yaml').\n2. Use the 'fabric-cli' or scripts to create channel artifacts.\n3. Launch the network by starting the necessary Docker containers and services using 'docker-compose' or Kubernetes.\n4. Instantiate and upgrade chaincode as needed.", + "How to run a Hyperledger Fabric network?": "To run a Hyperledger Fabric network:\n1. Start the network by running 'docker-compose up' or the appropriate command for your setup.\n2. Use the Fabric CLI tools or SDKs to interact with the network, including creating and joining channels, and submitting transactions.\n3. Monitor the network's health and performance using Fabric's built-in tools or external monitoring solutions.", + "How to ensure data privacy in Hyperledger Fabric?": "To ensure data privacy in Hyperledger Fabric:\n1. Use private data collections to restrict access to sensitive data.\n2. Implement access control policies and endorsement policies.\n3. Utilize encryption for data at rest and in transit.\n4. Regularly review and update security configurations and practices." +} + +def get_hyperledger_fabric_answer(question): + return responses.get(question, "Question not found in the database.") + +# TODO: Get all chats for a user in a paginated format +@router.post("/conversations") +def get_conversations(offset: int = 0, limit: int = 30, order: str = "updated") -> ResponseConversation: + pass + +# TODO: Get a single chat for a user +@router.post("/conversation/{id}") +def post_conversation(id:str): + pass + +@router.post("/conversation", response_model=ResponseConversation) +def post_conversation(item: RequestConversation) -> ResponseConversation: + return ResponseConversation( + id=item.id, + message=ResponseMessage( + content=get_hyperledger_fabric_answer(item.content), + type=1, + id=str(uuid.uuid4()) + ), + ) \ No newline at end of file diff --git a/src/core/utils.py b/src/core/utils.py index 9a09b85..88e24da 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -6,4 +6,4 @@ def load_yaml_file(file_name): file_path = os.path.join(base_path, file_name) with open(file_path, 'r') as f: - return yaml.safe_load(f) + return yaml.safe_load(f) \ No newline at end of file diff --git a/src/frontend/app/page.tsx b/src/frontend/app/page.tsx index 3f583b7..11c9d61 100644 --- a/src/frontend/app/page.tsx +++ b/src/frontend/app/page.tsx @@ -1,52 +1,11 @@ -"use client"; - -import React, { useState } from 'react'; import Sidebar from "@/components/sidebar"; -import ChatHeader from "@/components/chat-header"; -import WelcomeSection from "@/components/welcome-section"; -import ChatSection from "@/components/chat-section"; -import ChatBottomBar from '@/components/chat-bottom-bar'; - -interface Message { - request: string; - response: string; -} +import ChatPage from '@/components/chat-page'; export default function Home() { - const [messages, setMessages] = useState([]); - const chatSelected = false; - - const handleSend = (request: string) => { - const response = 'Both the Fabric documentation and Fabric samples rely heavily on a bash environment. The recommended path is to use WSL2 (Windows Subsystem for Linux version 2) to provide a native Linux environment and then you can follow the Linux prerequisites section (excluding the Linux Docker prerequisite as you already have Docker Desktop) and install them into your WSL2 linux distribution. WSL2 may not be installed by default; you can check and install WSL2 by going into “Programs and Features”, clicking on “Turn Windows features on or off” and ensuring that both “Windows Subsystem For Linux” and “Virtual Machine Platform” are selected. Next you will need to install a Linux distribution such as Ubuntu-20.04 and make sure it’s set to using version 2 of WSL. Refer to Install WSL for more information. Finally, you need to ensure Docker Desktop has integration enabled for your distribution so it can interact with Docker elements, such as a bash command window. To do this, open the Docker Desktop gui and go into settings, select Resources and them WSL Integration and ensure the checkbox for enable integration is checked. You should then see your WSL2 linux distribution listed (if you don’t then it is probably because it is still a WSL1 distribution and needs to be converted to WSL2) and you can then toggle the switch to enable integration for that distro. Refer to Docker Desktop WSL2 backend for more information'; - // To-do: Error Handling - // const response = await fetch('http://localhost:8080/chat', { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify({ message }), - // }); - // const data = await response.json(); - // setMessages([...messages, { request: message, response: data.response }]); - - const newMessage = { request, response }; - setMessages((prevMessages) => [...prevMessages, newMessage]); - }; - return (
-
- -
- {chatSelected ? : } -
-
-
- -
-
-
+
); } diff --git a/src/frontend/components/chat-bottom-bar.tsx b/src/frontend/components/chat-bottom-bar.tsx index cd22202..f99b1ff 100644 --- a/src/frontend/components/chat-bottom-bar.tsx +++ b/src/frontend/components/chat-bottom-bar.tsx @@ -5,18 +5,24 @@ import { Button } from './ui/button'; import { ArrowUp, Paperclip } from 'lucide-react'; import TextareaAutosize from 'react-textarea-autosize'; import { useWindowSize } from '@/hooks/useWindowSize'; +import { Message } from '@/lib/types'; +import { v4 as uuidv4 } from 'uuid'; +import { handleSend } from '@/lib/apis/fetcher'; interface Props { - onSend: (message: string) => void; + onSend: (newMessage: Message) => void } const ChatBottomBar = ({ onSend }: Props) => { const [isMounted, setIsMounted] = useState(false); - const [message, setMessage] = useState(''); + const [message, setMessage] = useState({ + content: '', + type: 0, + id: "-1" + }); const { isMobile } = useWindowSize(); useEffect(() => { - console.log('ChatBottomBar onSend:', onSend); setIsMounted(true); }, [isMounted]); @@ -25,60 +31,69 @@ const ChatBottomBar = ({ onSend }: Props) => { } const handleChange = (e: React.ChangeEvent) => { - setMessage(e.target.value); + setMessage({ + ...message, + content: e.target.value + }); }; - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (message.trim()) { - onSend(message); - setMessage(''); + const handleSubmit = (e?: React.FormEvent | React.KeyboardEvent) => { + if (e) { + e.preventDefault(); + if ('key' in e && e.key !== 'Enter') { + return; + } } - }; - const handleSendButtonClick = () => { - if (message.trim()) { - onSend(message); - setMessage(''); - } - }; + if (message.content.trim()) { + const updatedMessage = { + ...message, + id: uuidv4(), + }; - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSubmit(e as unknown as React.FormEvent); + handleSend(updatedMessage).then((value) => { + if (value !== undefined) { + onSend(updatedMessage) + onSend(value.message) + } + }).catch().finally(() => setMessage({ + ...message, + content: '', + })) } }; return ( -
-
-
- - - - -
AIFAQ can make mistakes. Check important information.
-
+
+
+ + { + if (e.key === 'Enter' && !e.shiftKey) { + handleSubmit(e); + } + }} + /> + + +
AIFAQ can make mistakes. Check important information.
); }; diff --git a/src/frontend/components/chat-page.tsx b/src/frontend/components/chat-page.tsx new file mode 100644 index 0000000..fe79606 --- /dev/null +++ b/src/frontend/components/chat-page.tsx @@ -0,0 +1,29 @@ +'use client'; + +import React, { useState } from 'react' +import ChatHeader from './chat-header' +import { Message } from '@/lib/types'; +import ChatBottomBar from './chat-bottom-bar'; +import ChatSection from './chat-section'; + +type Props = {} + +const ChatPage = (props: Props) => { + const [messages, setMessages] = useState([]); + + const updateMessages = (newMessage: Message) => { + setMessages((prevMessages) => [...prevMessages, newMessage]); + }; + + return ( +
+ +
+ +
+ +
+ ) +} + +export default ChatPage \ No newline at end of file diff --git a/src/frontend/components/chat-request.tsx b/src/frontend/components/chat-request.tsx index 42cd213..3e84caa 100644 --- a/src/frontend/components/chat-request.tsx +++ b/src/frontend/components/chat-request.tsx @@ -6,8 +6,8 @@ interface Props { const ChatRequest = ({ request }: Props) => { return ( -
-
+
+

{request}

diff --git a/src/frontend/components/chat-response.tsx b/src/frontend/components/chat-response.tsx index f36b524..5d3be15 100644 --- a/src/frontend/components/chat-response.tsx +++ b/src/frontend/components/chat-response.tsx @@ -8,7 +8,7 @@ interface Props { const ChatResponse = ({ response }: Props) => (
-
+
diff --git a/src/frontend/components/chat-section.tsx b/src/frontend/components/chat-section.tsx index ba0c7cf..1a821d1 100644 --- a/src/frontend/components/chat-section.tsx +++ b/src/frontend/components/chat-section.tsx @@ -1,13 +1,7 @@ -"use client"; - -import React, {useRef, useEffect } from 'react'; -import ChatResponse from './chat-response'; +import React, { useRef, useEffect } from 'react'; +import { Message } from '@/lib/types'; import ChatRequest from './chat-request'; - -interface Message { - request: string; - response: string; -} +import ChatResponse from './chat-response'; interface ChatSectionProps { messages: Message[]; @@ -23,12 +17,13 @@ const ChatSection: React.FC = ({ messages }) => { }, [messages]); return ( -
-
- {messages.map((msg, index) => ( -
- - +
+
+ {messages.map((message, index) => ( +
+ { + message.type === 0 ? : + }
))}
@@ -36,4 +31,4 @@ const ChatSection: React.FC = ({ messages }) => { ); }; -export default ChatSection; +export default ChatSection; \ No newline at end of file diff --git a/src/frontend/lib/apis/fetcher.ts b/src/frontend/lib/apis/fetcher.ts new file mode 100644 index 0000000..86c05b6 --- /dev/null +++ b/src/frontend/lib/apis/fetcher.ts @@ -0,0 +1,16 @@ +import { SERVER_BASE_URL } from "../const"; +import { Message } from "@/lib/types"; + +export async function handleSend(message: Message) { + return fetch(SERVER_BASE_URL + '/conversation', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(message) + }).then((response) => { + return response.json() + }).catch((err) => { + console.log(err) + }) +} \ No newline at end of file diff --git a/src/frontend/lib/const.ts b/src/frontend/lib/const.ts new file mode 100644 index 0000000..ec61bd6 --- /dev/null +++ b/src/frontend/lib/const.ts @@ -0,0 +1 @@ +export const SERVER_BASE_URL = process.env.NODE_ENV === "development" ? "http://127.0.0.1:8080/api/test" : ''; \ No newline at end of file diff --git a/src/frontend/lib/types.ts b/src/frontend/lib/types.ts new file mode 100644 index 0000000..4780083 --- /dev/null +++ b/src/frontend/lib/types.ts @@ -0,0 +1,6 @@ +export interface Message { + /// 0: user | 1: agent + type: 0 | 1 + id: string + content: string +} \ No newline at end of file diff --git a/src/frontend/next.config.mjs b/src/frontend/next.config.mjs index d77ffab..2d7ff8a 100644 --- a/src/frontend/next.config.mjs +++ b/src/frontend/next.config.mjs @@ -1,6 +1,17 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - output : "standalone", + output: "standalone", + rewrites: async () => { + return [ + { + source: "/api/:path*", + destination: + process.env.NODE_ENV === "development" + ? "http://127.0.0.1:8080/api/:path*" + : "/api/", + } + ]; + }, }; export default nextConfig; diff --git a/src/frontend/package.json b/src/frontend/package.json index e640551..9d725f5 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -26,6 +26,7 @@ "sharp": "^0.33.4", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", + "uuid": "^10.0.0", "vaul": "^0.9.1" }, "devDependencies": { @@ -33,6 +34,7 @@ "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "@types/uuid": "^10.0.0", "eslint": "^8", "eslint-config-next": "14.2.4", "lodash.debounce": "^4.0.8", diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 24c4f4c..88df1d7 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -656,6 +656,11 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.2.0.tgz#44356312aea8852a3a82deebdacd52ba614ec07a" @@ -3334,6 +3339,11 @@ util-deprecate@^1.0.2: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + vaul@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/vaul/-/vaul-0.9.1.tgz#3640198e04636b209b1f907fcf3079bec6ecc66b"