use api key instead of project secret

This commit is contained in:
ramnique 2025-01-27 10:59:07 +05:30
parent 83591a690d
commit e3576489e3
5 changed files with 29 additions and 24 deletions

View file

@ -94,7 +94,7 @@ Before running RowBoat, ensure you have:
client = Client( client = Client(
host="http://localhost:3000", host="http://localhost:3000",
project_id="<PROJECT_ID>", project_id="<PROJECT_ID>",
project_secret="<PROJECT_SECRET>" api_key="<API_KEY>" # Generate this from /projects/<PROJECT_ID>/config
) )
# Simple chat interaction # Simple chat interaction
@ -108,12 +108,12 @@ Before running RowBoat, ensure you have:
You can use the API directly at [http://localhost:3000/api/v1/](http://localhost:3000/api/v1/) You can use the API directly at [http://localhost:3000/api/v1/](http://localhost:3000/api/v1/)
- Project ID is available in the URL of the project page - Project ID is available in the URL of the project page
- Project Secret is available in the project config page - API Key can be generated from the project config page at `/projects/<PROJECT_ID>/config`
```bash ```bash
curl --location 'http://localhost:3000/api/v1/<PROJECT_ID>/chat' \ curl --location 'http://localhost:3000/api/v1/<PROJECT_ID>/chat' \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--header 'Authorization: Bearer <PROJECT_SECRET>' \ --header 'Authorization: Bearer <API_KEY>' \
--data '{ --data '{
"messages": [ "messages": [
{ {

View file

@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "rowboat" name = "rowboat"
version = "0.2.1" version = "1.0.0"
authors = [ authors = [
{ name = "Your Name", email = "your.email@example.com" }, { name = "Your Name", email = "your.email@example.com" },
] ]

View file

@ -14,11 +14,11 @@ from .schema import (
class Client: class Client:
def __init__(self, host: str, project_id: str, project_secret: str) -> None: def __init__(self, host: str, project_id: str, api_key: str) -> None:
self.base_url: str = f'{host}/api/v1/{project_id}/chat' self.base_url: str = f'{host}/api/v1/{project_id}/chat'
self.headers: Dict[str, str] = { self.headers: Dict[str, str] = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': f'Bearer {project_secret}' 'Authorization': f'Bearer {api_key}'
} }
def _call_api( def _call_api(
@ -167,8 +167,8 @@ def weather_lookup_tool(city_name: str) -> str:
if __name__ == "__main__": if __name__ == "__main__":
host: str = "<HOST>" host: str = "<HOST>"
project_id: str = "<PROJECT_ID>" project_id: str = "<PROJECT_ID>"
project_secret: str = "<PROJECT_SECRET>" api_key: str = "<API_KEY>"
client = Client(host, project_id, project_secret) client = Client(host, project_id, api_key)
tools: Dict[str, Callable[..., str]] = { tools: Dict[str, Callable[..., str]] = {
'weather_lookup': weather_lookup_tool 'weather_lookup': weather_lookup_tool

View file

@ -1,22 +1,26 @@
import { NextRequest } from "next/server"; import { NextRequest } from "next/server";
import { projectsCollection } from "@/app/lib/mongodb"; import { apiKeysCollection, projectsCollection } from "@/app/lib/mongodb";
export async function authCheck(projectId: string, req: NextRequest, handler: () => Promise<Response>): Promise<Response> { export async function authCheck(projectId: string, req: NextRequest, handler: () => Promise<Response>): Promise<Response> {
const authHeader = req.headers.get('Authorization'); const authHeader = req.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) { if (!authHeader?.startsWith('Bearer ')) {
return Response.json({ error: "Authorization header must be a Bearer token" }, { status: 400 }); return Response.json({ error: "Authorization header must be a Bearer token" }, { status: 400 });
} }
const token = authHeader.split(' ')[1]; const key = authHeader.split(' ')[1];
if (!token) { if (!key) {
return Response.json({ error: "Missing API key in request" }, { status: 400 }); return Response.json({ error: "Missing API key in request" }, { status: 400 });
} }
// check the key in project settings // check if api key is valid
const project = await projectsCollection.findOne({ // while also updating last used timestamp
_id: projectId, const result = await apiKeysCollection.findOneAndUpdate(
secret: token, {
}); projectId,
if (!project) { key,
},
{ $set: { lastUsedAt: new Date().toISOString() } }
);
if (!result) {
return Response.json({ error: "Invalid API key" }, { status: 403 }); return Response.json({ error: "Invalid API key" }, { status: 403 });
} }

View file

@ -7,6 +7,9 @@ import { getProjectConfig, updateProjectName, updateWebhookUrl, createApiKey, de
import { CopyButton } from "@/app/lib/components/copy-button"; import { CopyButton } from "@/app/lib/components/copy-button";
import { EditableField } from "@/app/lib/components/editable-field"; import { EditableField } from "@/app/lib/components/editable-field";
import { EyeIcon, EyeOffIcon, CopyIcon, MoreVerticalIcon, PlusIcon, EllipsisVerticalIcon } from "lucide-react"; import { EyeIcon, EyeOffIcon, CopyIcon, MoreVerticalIcon, PlusIcon, EllipsisVerticalIcon } from "lucide-react";
import { WithStringId, ApiKey } from "@/app/lib/types";
import { z } from "zod";
import { RelativeTime } from "@primer/react";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Project config", title: "Project config",
@ -142,11 +145,7 @@ export function ApiKeysSection({
}: { }: {
projectId: string; projectId: string;
}) { }) {
const [keys, setKeys] = useState<Array<{ const [keys, setKeys] = useState<WithStringId<z.infer<typeof ApiKey>>[]>([]);
_id: string;
key: string;
createdAt: string;
}>>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [message, setMessage] = useState<{ const [message, setMessage] = useState<{
type: 'success' | 'error' | 'info'; type: 'success' | 'error' | 'info';
@ -254,9 +253,11 @@ export function ApiKeysSection({
<ApiKeyDisplay apiKey={key.key} /> <ApiKeyDisplay apiKey={key.key} />
</div> </div>
<div className="flex-1 p-2"> <div className="flex-1 p-2">
{new Date(key.createdAt).toLocaleDateString()} <RelativeTime date={new Date(key.createdAt)} />
</div>
<div className="flex-1 p-2">
{key.lastUsedAt ? <RelativeTime date={new Date(key.lastUsedAt)} /> : 'Never'}
</div> </div>
<div className="flex-1 p-2">Never</div>
<div className="w-10 p-2"> <div className="w-10 p-2">
<Dropdown> <Dropdown>
<DropdownTrigger> <DropdownTrigger>