Scaffolding of Natural Language Search
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
"axios": "^1.7.2",
|
||||
"formik": "^2.4.6",
|
||||
"framer-motion": "^11.2.10",
|
||||
"openai": "^4.53.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
|
||||
5810
client/pnpm-lock.yaml
generated
5810
client/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
146
client/src/components/EcoconnectSearch.tsx
Normal file
146
client/src/components/EcoconnectSearch.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Kbd,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
useDisclosure,
|
||||
} from "@nextui-org/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { MagnifyingGlassIcon } from "../icons";
|
||||
import config from "../config";
|
||||
import instance from "../security/http";
|
||||
import EcoconnectFullLogo from "./EcoconnectFullLogo";
|
||||
|
||||
export default function EcoconnectSearch() {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [aiResponse, setAiResponse] = useState("");
|
||||
const [isQueryLoading, setIsQueryLoading] = useState(false);
|
||||
|
||||
const {
|
||||
isOpen: isAiDialogOpen,
|
||||
onOpen: onAiDialogOpen,
|
||||
onOpenChange: onAiDialogOpenChange,
|
||||
} = useDisclosure();
|
||||
|
||||
const dialogOpenChange = () => {
|
||||
onAiDialogOpenChange();
|
||||
setSearchQuery("");
|
||||
setAiResponse("");
|
||||
};
|
||||
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
if (event.ctrlKey && event.key === "s") {
|
||||
event.preventDefault();
|
||||
onAiDialogOpen();
|
||||
}
|
||||
};
|
||||
|
||||
const executeSearch = async () => {
|
||||
if (searchQuery.length <= 0) return;
|
||||
setIsQueryLoading(true);
|
||||
instance
|
||||
.get(
|
||||
`${config.serverAddress}/connections/openai-chat-completion/${searchQuery}`
|
||||
)
|
||||
.then((response) => {
|
||||
console.log(response.data.response);
|
||||
setAiResponse(response.data.response);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsQueryLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleKeyPress);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyPress);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="-mb-1">
|
||||
<Button
|
||||
size="sm"
|
||||
className="p-0"
|
||||
variant="light"
|
||||
isDisabled={isAiDialogOpen}
|
||||
onPress={() => {
|
||||
onAiDialogOpen();
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
size="sm"
|
||||
disabled
|
||||
className="w-44 h-min"
|
||||
startContent={<MagnifyingGlassIcon />}
|
||||
endContent={
|
||||
<div className="-mr-1">
|
||||
<Kbd keys={["ctrl"]}>S</Kbd>
|
||||
</div>
|
||||
}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</Button>
|
||||
<Modal
|
||||
isOpen={isAiDialogOpen}
|
||||
onOpenChange={dialogOpenChange}
|
||||
closeButton={<></>}
|
||||
placement="top"
|
||||
>
|
||||
<ModalContent>
|
||||
{() => {
|
||||
return (
|
||||
<>
|
||||
<ModalBody>
|
||||
<div className="py-4 flex flex-col gap-4">
|
||||
<Input
|
||||
placeholder="Search..."
|
||||
size="lg"
|
||||
value={searchQuery}
|
||||
onValueChange={setSearchQuery}
|
||||
isDisabled={isQueryLoading}
|
||||
onKeyDown={(keyEvent) => {
|
||||
if (keyEvent.key == "Enter") {
|
||||
executeSearch();
|
||||
}
|
||||
}}
|
||||
endContent={
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="flat"
|
||||
onPress={executeSearch}
|
||||
isLoading={isQueryLoading}
|
||||
className="-mr-2"
|
||||
>
|
||||
<MagnifyingGlassIcon />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
{aiResponse.length > 0 && (
|
||||
<p className="bg-neutral-50 p-4 border-2 rounded-xl">
|
||||
{aiResponse}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter className="bg-red-50 dark:bg-red-950 w-full h-full">
|
||||
<div className="w-full h-full flex flex-row justify-between *:my-auto">
|
||||
<EcoconnectFullLogo />
|
||||
<p className="text-lg text-red-900 dark:text-red-100">
|
||||
Natural Language Search
|
||||
</p>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { retrieveUserInformation } from "../security/users";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import EcoconnectFullLogo from "./EcoconnectFullLogo";
|
||||
import EcoconnectSearch from "./EcoconnectSearch";
|
||||
|
||||
export default function NavigationBar() {
|
||||
const [userProfileImageURL, setUserProfileImageURL] = useState("");
|
||||
@@ -58,7 +59,7 @@ export default function NavigationBar() {
|
||||
}
|
||||
>
|
||||
<div className="relative bg-primary-50 dark:bg-primary-800 border-2 border-primary-100 dark:border-primary-950 shadow-lg w-full h-full rounded-xl flex flex-col justify-center p-1">
|
||||
<div className=" w-full flex flex-row justify-between gap-4">
|
||||
<div className="w-full flex flex-row justify-between gap-4">
|
||||
<div className="flex flex-row gap-0 my-auto *:my-auto">
|
||||
<Button
|
||||
variant="light"
|
||||
@@ -109,60 +110,65 @@ export default function NavigationBar() {
|
||||
</div>
|
||||
</div>
|
||||
{userInformation && (
|
||||
<div className="my-auto pr-1">
|
||||
<Dropdown placement="bottom" backdrop="blur">
|
||||
<DropdownTrigger>
|
||||
<Avatar src={userProfileImageURL} as="button" size="sm" />
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label="Profile Actions">
|
||||
<DropdownSection showDivider>
|
||||
<DropdownItem key="account-overview" isReadOnly>
|
||||
<div className="flex flex-col gap-2 text-center *:mx-auto p-2">
|
||||
<Avatar
|
||||
src={userProfileImageURL}
|
||||
as="button"
|
||||
size="lg"
|
||||
isBordered
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p>Signed in as</p>
|
||||
<p className="text-lg font-bold">
|
||||
<span>{userInformation.firstName}</span>{" "}
|
||||
<span>{userInformation.lastName}</span>
|
||||
</p>
|
||||
<p className="opacity-50">{userInformation.email}</p>
|
||||
<div className="my-auto pr-1 flex flex-row justify-end">
|
||||
<div className="flex flex-row gap-2 w-min">
|
||||
<EcoconnectSearch />
|
||||
<Dropdown placement="bottom" backdrop="blur">
|
||||
<DropdownTrigger>
|
||||
<Avatar src={userProfileImageURL} as="button" size="sm" />
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label="Profile Actions">
|
||||
<DropdownSection showDivider>
|
||||
<DropdownItem key="account-overview" isReadOnly>
|
||||
<div className="flex flex-col gap-2 text-center *:mx-auto p-2 w-full">
|
||||
<Avatar
|
||||
src={userProfileImageURL}
|
||||
as="button"
|
||||
size="lg"
|
||||
isBordered
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p>Signed in as</p>
|
||||
<p className="text-lg font-bold">
|
||||
<span>{userInformation.firstName}</span>{" "}
|
||||
<span>{userInformation.lastName}</span>
|
||||
</p>
|
||||
<p className="opacity-50">
|
||||
{userInformation.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownItem>
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="dashboard"
|
||||
title="Dashboard"
|
||||
startContent={<RocketLaunchIcon />}
|
||||
onPress={() => {
|
||||
navigate("/springboard");
|
||||
}}
|
||||
/>
|
||||
<DropdownItem
|
||||
key="manage-account"
|
||||
title="Manage your account"
|
||||
startContent={<PencilSquareIcon />}
|
||||
onPress={() => {
|
||||
navigate("/manage-account");
|
||||
}}
|
||||
/>
|
||||
</DropdownSection>
|
||||
<DropdownItem
|
||||
key="dashboard"
|
||||
title="Dashboard"
|
||||
startContent={<RocketLaunchIcon />}
|
||||
key="signout"
|
||||
startContent={<ArrowRightStartOnRectangleIcon />}
|
||||
color="danger"
|
||||
title="Sign out"
|
||||
onPress={() => {
|
||||
navigate("/springboard");
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
}}
|
||||
/>
|
||||
<DropdownItem
|
||||
key="manage-account"
|
||||
title="Manage your account"
|
||||
startContent={<PencilSquareIcon />}
|
||||
onPress={() => {
|
||||
navigate("/manage-account");
|
||||
}}
|
||||
/>
|
||||
</DropdownSection>
|
||||
<DropdownItem
|
||||
key="signout"
|
||||
startContent={<ArrowRightStartOnRectangleIcon />}
|
||||
color="danger"
|
||||
title="Sign out"
|
||||
onPress={() => {
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
}}
|
||||
/>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!userInformation && doneLoading && (
|
||||
|
||||
18
server/connections/apiKey.js
Normal file
18
server/connections/apiKey.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const axios = require("axios");
|
||||
|
||||
// Adam's personal API key server access
|
||||
// Requires connection to private tailscale subnet.
|
||||
// no abusing of my api keys or i abuse you 🔫
|
||||
async function getApiKey(serviceUrl) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
"http://mommy.rya-orfe.ts.net:8069/" + serviceUrl
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error retrieving API key:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { getApiKey };
|
||||
@@ -1,21 +1,10 @@
|
||||
const axios = require("axios");
|
||||
const senderEmail = "ecoconnect@trial-ynrw7gy0qxol2k8e.mlsender.net";
|
||||
|
||||
async function getApiKey() {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
"http://mommy.rya-orfe.ts.net:8069/mailersend_api_key"
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error retrieving API key:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const { getApiKey } = require("./apiKey");
|
||||
|
||||
async function sendEmail(recipientEmail, title, content) {
|
||||
try {
|
||||
const apiKey = await getApiKey();
|
||||
const apiKey = await getApiKey("mailersend_api_key");
|
||||
const response = await axios.post(
|
||||
"https://api.mailersend.com/v1/email",
|
||||
{
|
||||
|
||||
20
server/connections/openai.js
Normal file
20
server/connections/openai.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const OpenAI = require("openai");
|
||||
const { getApiKey } = require("./apiKey");
|
||||
|
||||
async function openAiChatCompletion(query) {
|
||||
const openai = new OpenAI({ apiKey: await getApiKey("openai_api_key") });
|
||||
const completion = await openai.chat.completions.create({
|
||||
messages: [
|
||||
{ role: "system", content: "You are a helpful assistant." },
|
||||
{ role: "user", content: query },
|
||||
],
|
||||
model: "gpt-4o-mini",
|
||||
});
|
||||
|
||||
let response = completion.choices[0].message.content;
|
||||
console.log(response);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
module.exports = { openAiChatCompletion };
|
||||
@@ -37,6 +37,9 @@ app.use("/schedule", schedulesRoute);
|
||||
const HBCformRoute = require("./routes/hbcform");
|
||||
app.use("/hbcform", HBCformRoute);
|
||||
|
||||
const connections = require("./routes/connections");
|
||||
app.use("/connections", connections);
|
||||
|
||||
db.sequelize
|
||||
.sync({ alter: true })
|
||||
.then(() => {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"multer": "1.4.5-lts.1",
|
||||
"mysql2": "^3.10.1",
|
||||
"nodemon": "^3.1.3",
|
||||
"openai": "^4.53.2",
|
||||
"sequelize": "^6.37.3",
|
||||
"sharp": "^0.33.4",
|
||||
"uuid": "^10.0.0",
|
||||
|
||||
105
server/pnpm-lock.yaml
generated
105
server/pnpm-lock.yaml
generated
@@ -35,6 +35,9 @@ dependencies:
|
||||
nodemon:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3
|
||||
openai:
|
||||
specifier: ^4.53.2
|
||||
version: 4.53.2
|
||||
sequelize:
|
||||
specifier: ^6.37.3
|
||||
version: 6.37.3(mysql2@3.10.1)
|
||||
@@ -261,6 +264,19 @@ packages:
|
||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||
dev: false
|
||||
|
||||
/@types/node-fetch@2.6.11:
|
||||
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
|
||||
dependencies:
|
||||
'@types/node': 20.14.6
|
||||
form-data: 4.0.0
|
||||
dev: false
|
||||
|
||||
/@types/node@18.19.42:
|
||||
resolution: {integrity: sha512-d2ZFc/3lnK2YCYhos8iaNIYu9Vfhr92nHiyJHRltXWjXUBjEE+A4I58Tdbnw4VhggSW+2j5y5gTrLs4biNnubg==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
dev: false
|
||||
|
||||
/@types/node@20.14.6:
|
||||
resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==}
|
||||
dependencies:
|
||||
@@ -271,6 +287,13 @@ packages:
|
||||
resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==}
|
||||
dev: false
|
||||
|
||||
/abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
dependencies:
|
||||
event-target-shim: 5.0.1
|
||||
dev: false
|
||||
|
||||
/accepts@1.3.8:
|
||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -279,6 +302,13 @@ packages:
|
||||
negotiator: 0.6.3
|
||||
dev: false
|
||||
|
||||
/agentkeepalive@4.5.0:
|
||||
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
dependencies:
|
||||
humanize-ms: 1.2.1
|
||||
dev: false
|
||||
|
||||
/anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -602,6 +632,11 @@ packages:
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/event-target-shim@5.0.1:
|
||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/express@4.19.2:
|
||||
resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
@@ -673,6 +708,10 @@ packages:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/form-data-encoder@1.7.2:
|
||||
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
|
||||
dev: false
|
||||
|
||||
/form-data@4.0.0:
|
||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -682,6 +721,14 @@ packages:
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/formdata-node@4.4.1:
|
||||
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
|
||||
engines: {node: '>= 12.20'}
|
||||
dependencies:
|
||||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 4.0.0-beta.3
|
||||
dev: false
|
||||
|
||||
/forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -773,6 +820,12 @@ packages:
|
||||
toidentifier: 1.0.1
|
||||
dev: false
|
||||
|
||||
/humanize-ms@1.2.1:
|
||||
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
dev: false
|
||||
|
||||
/iconv-lite@0.4.24:
|
||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1033,6 +1086,23 @@ packages:
|
||||
engines: {node: ^18 || ^20 || >= 21}
|
||||
dev: false
|
||||
|
||||
/node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
dev: false
|
||||
|
||||
/node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-gyp-build@4.8.1:
|
||||
resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==}
|
||||
hasBin: true
|
||||
@@ -1076,6 +1146,21 @@ packages:
|
||||
ee-first: 1.1.1
|
||||
dev: false
|
||||
|
||||
/openai@4.53.2:
|
||||
resolution: {integrity: sha512-ohYEv6OV3jsFGqNrgolDDWN6Ssx1nFg6JDJQuaBFo4SL2i+MBoOQ16n2Pq1iBF5lH1PKnfCIOfqAGkmzPvdB9g==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/node': 18.19.42
|
||||
'@types/node-fetch': 2.6.11
|
||||
abort-controller: 3.0.0
|
||||
agentkeepalive: 4.5.0
|
||||
form-data-encoder: 1.7.2
|
||||
formdata-node: 4.4.1
|
||||
node-fetch: 2.7.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
|
||||
/parseurl@1.3.3:
|
||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -1403,6 +1488,10 @@ packages:
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: false
|
||||
|
||||
/tslib@2.6.3:
|
||||
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
|
||||
requiresBuild: true
|
||||
@@ -1468,6 +1557,22 @@ packages:
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/web-streams-polyfill@4.0.0-beta.3:
|
||||
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
||||
engines: {node: '>= 14'}
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
||||
/whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
dev: false
|
||||
|
||||
/wkx@0.5.0:
|
||||
resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==}
|
||||
dependencies:
|
||||
|
||||
22
server/routes/connections.js
Normal file
22
server/routes/connections.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const express = require("express");
|
||||
const { openAiChatCompletion } = require("../connections/openai");
|
||||
const { validateToken } = require("../middlewares/auth");
|
||||
const router = express.Router();
|
||||
|
||||
router.get(
|
||||
"/openai-chat-completion/:query",
|
||||
validateToken,
|
||||
async (req, res) => {
|
||||
let data = req.params.query;
|
||||
console.log(data);
|
||||
try {
|
||||
let chatResponse = await openAiChatCompletion(data);
|
||||
res.json({ response: chatResponse });
|
||||
} catch (error) {
|
||||
console.error("Error with AI:", error);
|
||||
res.status(500).json({ message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user