Skip to content

Commit

Permalink
support for bot api
Browse files Browse the repository at this point in the history
  • Loading branch information
n4ze3m committed Oct 14, 2023
1 parent 1b88dd4 commit 0452983
Show file tree
Hide file tree
Showing 15 changed files with 439 additions and 44 deletions.
2 changes: 1 addition & 1 deletion app/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "app",
"private": true,
"version": "1.1.0",
"version": "1.1.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
1 change: 1 addition & 0 deletions app/ui/public/providers/api.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions app/ui/src/@types/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ export type BotSettings = {
bot_protect: boolean;
use_rag: boolean;
};


export type BotIntegrationAPI = {
is_api_enabled: boolean;
data: {
public_url: string | null;
api_key: string | null;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const ApiPlaygroundComponent: React.FC = () => {
return (
<div className=" min-h-screen ">
<div className="border rounded-md p-4 max-w-screen-xl mx-auto">
<h1 className="text-xl font-bold mb-4">POST /chat/completions</h1>

<div className="grid sm:grid-cols-1 md:grid-cols-2 gap-4">
<div className="border rounded-md p-4">
<h2 className="text-lg font-semibold mb-2">LEFT PANEL</h2>
{/* Content of left panel */}
</div>

<div className="border rounded-md p-4">
<h2 className="text-lg font-semibold mb-2">RIGHT PANEL</h2>
{/* Content of right panel */}
</div>
</div>
</div>
</div>
)
}


export default ApiPlaygroundComponent;
18 changes: 18 additions & 0 deletions app/ui/src/components/Bot/Integration/API/Index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import NoApiKeyComponent from "./NoApiKeyComponent";
import { BotIntegrationAPI } from "../../../../@types/bot";
import ApiPlaygroundComponent from "./ApiPlaygroundComponent";

const IntegrationAPIBody: React.FC<BotIntegrationAPI> = ({
is_api_enabled,
}) => {
return (
<div className="p-4">
<h2 className="text-2xl font-semibold mb-4 text-left">API Integration</h2>
{!is_api_enabled && <NoApiKeyComponent />}
{is_api_enabled && <ApiPlaygroundComponent />}
</div>
);
};

export default IntegrationAPIBody;
52 changes: 52 additions & 0 deletions app/ui/src/components/Bot/Integration/API/NoApiKeyComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { notification } from "antd";
import React from "react";
import api from "../../../../services/api";
import { useParams } from "react-router-dom";

const NoApiKeyComponent: React.FC = () => {
const client = useQueryClient();
const param = useParams<{ id: string }>();
const { mutate: generateAPIKey, isLoading: isGeneratingAPIKey } = useMutation(
async () => {
const response = await api.post(`/bot/integration/${param.id}/api`);
return response.data;
},
{
onSuccess: () => {
notification.success({
message: "Success",
description: "API key generated successfully.",
});
client.invalidateQueries(["getBotIntegrationAPI"]);
},
onError: (e) => {
console.log(e);
notification.error({
message: "Error",
description: "Something went wrong while generating the API key.",
});
},
}
);

return (
<div className="m-0 p-0 h-screen flex flex-col justify-start items-center">
<div className="mt-20 p-8 border bg-gray-50 border-gray-300 rounded-lg">
<h1 className="text-2xl font-semibold mb-4">No API Key Found</h1>
<p className="mb-4">You need to generate an API key to get started.</p>
<button
disabled={isGeneratingAPIKey}
onClick={() => {
generateAPIKey();
}}
className="bg-indigo-600 w-full text-center text-white px-4 py-2 rounded hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
{!isGeneratingAPIKey ? "Generate API Key" : "Generating API Key..."}
</button>
</div>
</div>
);
};

export default NoApiKeyComponent;
33 changes: 31 additions & 2 deletions app/ui/src/components/Bot/Integration/IntegrationGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ export const IntegrationGrid: React.FC<Props> = ({ data }) => {
))}

<Link
to={`/bot/${param.id}/embed`}
className="relative group bg-white p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-500 rounded-lg overflow-hidden border hover:shadow-lg transition-shadow duration-300 ease-in-out cursor-pointer">
to={`/bot/${param.id}/embed`}
className="relative group bg-white p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-500 rounded-lg overflow-hidden border hover:shadow-lg transition-shadow duration-300 ease-in-out cursor-pointer"
>
<div>
<div className="mb-4">
<div className="flex items-center justify-between">
Expand All @@ -139,6 +140,34 @@ export const IntegrationGrid: React.FC<Props> = ({ data }) => {
</div>
</div>
</Link>

<Link
to={`/bot/${param.id}/integrations/api`}
className="relative group bg-white p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-500 rounded-lg overflow-hidden border hover:shadow-lg transition-shadow duration-300 ease-in-out cursor-pointer"
>
<div>
<div className="mb-4">
<div className="flex items-center justify-between">
<img
className="h-12 w-auto"
src="/providers/api.svg"
alt="API"
/>
</div>
</div>
<div className="sm:flex sm:items-center">
<div className="text-center sm:text-left">
<h3 className="text-lg leading-6 font-medium text-gray-900">
API
</h3>
<p className="mt-2 text-xs text-gray-500">
Customize your integration using our robust API. Connect and
expand the capabilities of your chatbot across platforms.
</p>
</div>
</div>
</div>
</Link>
</div>

{/* MODAL */}
Expand Down
9 changes: 9 additions & 0 deletions app/ui/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import RegisterRoot from "./routes/register";
import { QueryBoundaries } from "./components/Common/QueryBoundaries";
import SettingsApplicationRoot from "./routes/settings/application";
import SettingsTeamsRoot from "./routes/settings/teams";
import BotIntegrationAPIRoot from "./routes/bot/api";

const router = createHashRouter([
{
Expand Down Expand Up @@ -106,6 +107,14 @@ const router = createHashRouter([
</BotLayout>
),
},
{
path: "/bot/:id/integrations/api",
element: (
<BotLayout>
<BotIntegrationAPIRoot />
</BotLayout>
),
},
{
path: "/bot/:id/appearance",
element: (
Expand Down
39 changes: 39 additions & 0 deletions app/ui/src/routes/bot/api.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useQuery } from "@tanstack/react-query";
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
import api from "../../services/api";
import { SkeletonLoading } from "../../components/Common/SkeletonLoading";
import IntegrationAPIBody from "../../components/Bot/Integration/API/Index";
import { BotIntegrationAPI } from "../../@types/bot";

export default function BotIntegrationAPIRoot() {
const param = useParams<{ id: string }>();
const navigate = useNavigate();
const { data, status } = useQuery(
["getBotIntegrationAPI", param.id],
async () => {
const response = await api.get(`/bot/integration/${param.id}/api`);
return response.data as BotIntegrationAPI;
},
{
enabled: !!param.id,
}
);

React.useEffect(() => {
if (status === "error") {
navigate("/");
}
}, [status]);

return (
<div className="mx-auto my-3 w-full max-w-7xl">
{status === "loading" &&

<SkeletonLoading />

}
{status === "success" && <IntegrationAPIBody {...data} />}
</div>
);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dialoqbase",
"version": "1.1.0",
"version": "1.1.1",
"description": "Create chatbots with ease",
"scripts": {
"ui:dev": "pnpm run --filter ui dev",
Expand Down
146 changes: 146 additions & 0 deletions server/src/routes/api/v1/bot/integration/handlers/api.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { FastifyReply, FastifyRequest } from "fastify";
import { GetAPIIntergationRequest } from "./type";
import { randomBytes } from "crypto";

const generateAPIKey = (length = 32) => {
const charset =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const bytes = randomBytes(length);
let result = "";

for (let i = 0; i < length; i++) {
result += charset.charAt(bytes[i] % charset.length);
}

return result;
};

export const getAPIIntegrationHandler = async (
request: FastifyRequest<GetAPIIntergationRequest>,
reply: FastifyReply,
) => {
try {
const prisma = request.server.prisma;
const id = request.params.id;

const bot = await prisma.bot.findFirst({
where: {
id,
user_id: request.user.user_id,
},
});

if (!bot) {
return reply.status(404).send({
message: "Bot not found",
});
}

if (!bot.bot_api_key) {
return {
is_api_enabled: false,
data: {
public_url: null,
api_key: null,
},
};
}

return {
is_api_enabled: true,
data: {
public_url: bot.publicId,
api_key: bot.bot_api_key,
},
};
} catch (e) {
console.log(e);
return reply.status(500).send({ message: "Internal Server Error" });
}
};

export const generateAPIKeyHandler = async (
request: FastifyRequest<GetAPIIntergationRequest>,
reply: FastifyReply,
) => {
try {
const prisma = request.server.prisma;
const id = request.params.id;

const bot = await prisma.bot.findFirst({
where: {
id,
user_id: request.user.user_id,
},
});

if (!bot) {
return reply.status(404).send({
message: "Bot not found",
});
}

const bot_api_key = generateAPIKey();

await prisma.bot.update({
where: {
id,
},
data: {
bot_api_key,
},
});

return {
data: {
bot_api_key,
},
};
} catch (e) {
console.log(e);
return reply.status(500).send({ message: "Internal Server Error" });
}
};

export const regenerateAPIKeyHandler = async (
request: FastifyRequest<GetAPIIntergationRequest>,
reply: FastifyReply,
) => {
try {
const prisma = request.server.prisma;
const id = request.params.id;

const bot = await prisma.bot.findFirst({
where: {
id,
user_id: request.user.user_id,
},
});

if (!bot) {
return reply.status(404).send({
message: "Bot not found",
});
}

const bot_api_key = generateAPIKey();

await prisma.bot.update({
where: {
id,
},
data: {
bot_api_key,
},
});

return {
data: {
bot_api_key,
},
};
} catch (e) {
console.log(e);
return reply.status(500).send({ message: "Internal Server Error" });
}
};
Loading

0 comments on commit 0452983

Please sign in to comment.