diff --git a/README.md b/README.md index 866115b6..5466ac6b 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Before running RowBoat, ensure you have: client = Client( host="http://localhost:3000", project_id="", - project_secret="" + api_key="" # Generate this from /projects//config ) # 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/) - 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//config` ```bash curl --location 'http://localhost:3000/api/v1//chat' \ --header 'Content-Type: application/json' \ - --header 'Authorization: Bearer ' \ + --header 'Authorization: Bearer ' \ --data '{ "messages": [ { diff --git a/apps/python-sdk/pyproject.toml b/apps/python-sdk/pyproject.toml index 1c8cbc01..f7b40419 100644 --- a/apps/python-sdk/pyproject.toml +++ b/apps/python-sdk/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "rowboat" -version = "0.2.1" +version = "1.0.0" authors = [ { name = "Your Name", email = "your.email@example.com" }, ] diff --git a/apps/python-sdk/src/rowboat/client.py b/apps/python-sdk/src/rowboat/client.py index e15d9c96..39085cf7 100644 --- a/apps/python-sdk/src/rowboat/client.py +++ b/apps/python-sdk/src/rowboat/client.py @@ -14,11 +14,11 @@ from .schema import ( 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.headers: Dict[str, str] = { 'Content-Type': 'application/json', - 'Authorization': f'Bearer {project_secret}' + 'Authorization': f'Bearer {api_key}' } def _call_api( @@ -167,8 +167,8 @@ def weather_lookup_tool(city_name: str) -> str: if __name__ == "__main__": host: str = "" project_id: str = "" - project_secret: str = "" - client = Client(host, project_id, project_secret) + api_key: str = "" + client = Client(host, project_id, api_key) tools: Dict[str, Callable[..., str]] = { 'weather_lookup': weather_lookup_tool diff --git a/apps/rowboat/app/api/v1/utils.ts b/apps/rowboat/app/api/v1/utils.ts index 18b317ed..aa2161b2 100644 --- a/apps/rowboat/app/api/v1/utils.ts +++ b/apps/rowboat/app/api/v1/utils.ts @@ -1,22 +1,26 @@ 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): Promise { const authHeader = req.headers.get('Authorization'); if (!authHeader?.startsWith('Bearer ')) { return Response.json({ error: "Authorization header must be a Bearer token" }, { status: 400 }); } - const token = authHeader.split(' ')[1]; - if (!token) { + const key = authHeader.split(' ')[1]; + if (!key) { return Response.json({ error: "Missing API key in request" }, { status: 400 }); } - // check the key in project settings - const project = await projectsCollection.findOne({ - _id: projectId, - secret: token, - }); - if (!project) { + // check if api key is valid + // while also updating last used timestamp + const result = await apiKeysCollection.findOneAndUpdate( + { + projectId, + key, + }, + { $set: { lastUsedAt: new Date().toISOString() } } + ); + if (!result) { return Response.json({ error: "Invalid API key" }, { status: 403 }); } diff --git a/apps/rowboat/app/projects/[projectId]/config/app.tsx b/apps/rowboat/app/projects/[projectId]/config/app.tsx index e84a489c..5f74a923 100644 --- a/apps/rowboat/app/projects/[projectId]/config/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/config/app.tsx @@ -7,6 +7,9 @@ import { getProjectConfig, updateProjectName, updateWebhookUrl, createApiKey, de import { CopyButton } from "@/app/lib/components/copy-button"; import { EditableField } from "@/app/lib/components/editable-field"; 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 = { title: "Project config", @@ -142,11 +145,7 @@ export function ApiKeysSection({ }: { projectId: string; }) { - const [keys, setKeys] = useState>([]); + const [keys, setKeys] = useState>[]>([]); const [loading, setLoading] = useState(true); const [message, setMessage] = useState<{ type: 'success' | 'error' | 'info'; @@ -254,9 +253,11 @@ export function ApiKeysSection({
- {new Date(key.createdAt).toLocaleDateString()} + +
+
+ {key.lastUsedAt ? : 'Never'}
-
Never