From d5e2540e51c1f3f0912b813dfd07e7380c722c94 Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Thu, 25 Jun 2026 23:22:11 +0530
Subject: [PATCH] refactor(api): rename personal access tokens to API keys and
update related UI components for consistency
---
.../components/ApiKeyContent.tsx | 204 ++++++++++++------
.../components/CommunityPromptsContent.tsx | 6 +-
.../components/PromptsContent.tsx | 6 +-
.../public-chat-snapshots-mutation.atoms.ts | 2 +-
.../changelog/content/2026-02-09.mdx | 4 +-
.../public-chat-snapshots-empty-state.tsx | 4 +-
.../public-chat-snapshots-manager.tsx | 8 +-
.../settings/general-settings-manager.tsx | 5 +-
surfsense_web/messages/en.json | 4 +-
9 files changed, 162 insertions(+), 81 deletions(-)
diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx
index e63812423..9946f244f 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent.tsx
@@ -1,10 +1,20 @@
"use client";
-import { Check, Copy, Info, Plus, Trash2 } from "lucide-react";
+import { Check, Copy, Info, Trash2 } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { Alert, AlertDescription } from "@/components/ui/alert";
-import { Badge } from "@/components/ui/badge";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
+import { Card, CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
@@ -16,6 +26,7 @@ import {
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
+import { Spinner } from "@/components/ui/spinner";
import { usePats } from "@/hooks/use-pats";
import { copyToClipboard as copyToClipboardUtil } from "@/lib/utils";
@@ -26,6 +37,7 @@ export function ApiKeyContent() {
const [label, setLabel] = useState("");
const [expiresInDays, setExpiresInDays] = useState("");
const [copiedToken, setCopiedToken] = useState(false);
+ const [deleteTarget, setDeleteTarget] = useState<{ id: number; label: string } | null>(null);
const sortedTokens = useMemo(() => tokens, [tokens]);
@@ -51,93 +63,112 @@ export function ApiKeyContent() {
}
}, [createdToken]);
- const handleDelete = useCallback(
- async (id: number, tokenLabel: string) => {
- if (!window.confirm(`Delete personal access token "${tokenLabel}"? This cannot be undone.`)) {
- return;
- }
- await deleteToken(id);
- },
- [deleteToken]
- );
+ const handleConfirmDelete = useCallback(async () => {
+ if (!deleteTarget) return;
+
+ await deleteToken(deleteTarget.id);
+ setDeleteTarget(null);
+ }, [deleteTarget, deleteToken]);
return (
-
+
- Personal access tokens are long-lived credentials for extensions, Obsidian, and
- programmatic API clients. Copy a token when you create it; it is shown only once.
+ API keys let extensions, Obsidian, and other apps connect to SurfSense.
-
Personal access tokens
+
API keys
- Expired tokens stay listed until you delete them.
+ Expired API keys stay listed until you delete them.
setCreateOpen(true)}>
-
- Create token
+ Create API key
-
- {isLoading ? (
-
-
-
-
- ) : sortedTokens.length > 0 ? (
-
- {sortedTokens.map((token) => {
- const expiresAt = token.expires_at ? new Date(token.expires_at) : null;
- const isExpired = expiresAt ? expiresAt.getTime() <= Date.now() : false;
- return (
-
+ {isLoading ? (
+
+ {["skeleton-a", "skeleton-b"].map((key) => (
+
+
+
+
+
+
+
+ ))}
+
+ ) : sortedTokens.length > 0 ? (
+
+ {sortedTokens.map((token) => {
+ const expiresAt = token.expires_at ? new Date(token.expires_at) : null;
+ const isExpired = expiresAt ? expiresAt.getTime() <= Date.now() : false;
+ return (
+
+
-
-
{token.label}
- {isExpired ?
Expired : null}
+
+
+
+ {token.label}
+
+ {isExpired ? (
+
+ Expired
+
+ ) : null}
+
+
+ {token.prefix}...
+
+
+ Expires: {expiresAt ? expiresAt.toLocaleDateString() : "Never"} · Last used:{" "}
+ {token.last_used_at ? new Date(token.last_used_at).toLocaleString() : "Never"}
+
-
{token.prefix}...
-
- Expires: {expiresAt ? expiresAt.toLocaleDateString() : "Never"} · Last used:{" "}
- {token.last_used_at ? new Date(token.last_used_at).toLocaleString() : "Never"}
-
handleDelete(token.id, token.label)}
+ onClick={() => setDeleteTarget({ id: token.id, label: token.label })}
+ className="h-7 w-7 shrink-0 rounded-lg text-muted-foreground transition-opacity duration-150 hover:text-accent-foreground sm:opacity-0 sm:pointer-events-none sm:group-hover:opacity-100 sm:group-hover:pointer-events-auto"
>
-
+
-
- );
- })}
-
- ) : (
-
- No personal access tokens yet.
-
- )}
-
+
+
+ );
+ })}
+
+ ) : (
+
+ No API keys yet.
+
+ )}
- Create personal access token
+ Create API key
- Name this token so you can recognize where it is used later.
+ Name this API key so you can recognize where it is used later.
- Label
+ Name
- setCreateOpen(false)}>
+ setCreateOpen(false)}
+ disabled={isMutating}
+ className="text-sm h-9"
+ >
Cancel
-
- Create token
+
+ Create API key
+ {isMutating && }
@@ -171,16 +215,21 @@ export function ApiKeyContent() {
!open && setCreatedToken(null)}>
- Copy your token now
+ Copy your API key now
- This token is shown only once. Store it somewhere secure before closing this dialog.
+ This API key is shown only once. Store it somewhere secure before closing this dialog.
{createdToken?.token}
-
+
{copiedToken ? : }
@@ -189,6 +238,41 @@ export function ApiKeyContent() {
+
+
!open && setDeleteTarget(null)}
+ >
+
+
+ Delete API key?
+
+ {deleteTarget?.label} will be
+ permanently removed. This cannot be undone.
+
+
+
+ Cancel
+ {
+ event.preventDefault();
+ void handleConfirmDelete();
+ }}
+ >
+ {isMutating ? (
+
+
+ Deleting...
+
+ ) : (
+ "Delete"
+ )}
+
+
+
+
);
}
diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/CommunityPromptsContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/CommunityPromptsContent.tsx
index 56044de5b..f4454f343 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/CommunityPromptsContent.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/CommunityPromptsContent.tsx
@@ -38,13 +38,13 @@ export function CommunityPromptsContent() {
const list = prompts ?? [];
return (
-
+
Prompts shared by other users. Add any to your collection with one click.
{isLoading && (
-
+
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
@@ -76,7 +76,7 @@ export function CommunityPromptsContent() {
)}
{!isLoading && !isError && list.length > 0 && (
-
+
{list.map((prompt) => (
+
Create prompt templates triggered with in
@@ -276,7 +276,7 @@ export function PromptsContent() {
{isLoading && (
-
+
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
@@ -308,7 +308,7 @@ export function PromptsContent() {
)}
{!isLoading && !isError && list.length > 0 && (
-
+
{list.map((prompt) => (
({
toast.success("Public link deleted");
},
onError: (error: Error) => {
- console.error("Failed to delete public chat link:", error);
+ console.error("Failed to delete public chat:", error);
toast.error("Failed to delete public link");
},
}));
diff --git a/surfsense_web/changelog/content/2026-02-09.mdx b/surfsense_web/changelog/content/2026-02-09.mdx
index 3bbc6f45e..7ffef2b4a 100644
--- a/surfsense_web/changelog/content/2026-02-09.mdx
+++ b/surfsense_web/changelog/content/2026-02-09.mdx
@@ -15,9 +15,9 @@ This update brings **public sharing, image generation**, a redesigned Documents
#### Public Sharing
-- **Public Chat Links**: Share snapshots of chats via public links.
+- **Public Chats**: Share snapshots of chats via public links.
- **Sharing Permissions**: Search Space owners control who can create and manage public links.
-- **Link Management Page**: View and revoke all public chat links from Search Space Settings.
+- **Link Management Page**: View and revoke all public chats from Search Space Settings.
#### Auto (Load Balanced) Mode
diff --git a/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-empty-state.tsx b/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-empty-state.tsx
index 4e8ec5bb6..e8e8b6b12 100644
--- a/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-empty-state.tsx
+++ b/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-empty-state.tsx
@@ -1,12 +1,10 @@
-import { Link2Off } from "lucide-react";
-
interface PublicChatSnapshotsEmptyStateProps {
title?: string;
description?: string;
}
export function PublicChatSnapshotsEmptyState({
- title = "No public chat links",
+ title = "No public chats",
description = "When you create public links to share chats, they will appear here.",
}: PublicChatSnapshotsEmptyStateProps) {
return (
diff --git a/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-manager.tsx b/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-manager.tsx
index 3cf07c27a..f18a0f705 100644
--- a/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-manager.tsx
+++ b/surfsense_web/components/public-chat-snapshots/public-chat-snapshots-manager.tsx
@@ -115,7 +115,7 @@ export function PublicChatSnapshotsManager({
- Failed to load public chat links. Please try again later.
+ Failed to load public chats. Please try again later.
);
@@ -127,7 +127,7 @@ export function PublicChatSnapshotsManager({
- You don't have permission to view public chat links in this search space.
+ You don't have permission to view public chats in this search space.
);
@@ -140,8 +140,8 @@ export function PublicChatSnapshotsManager({
- Public chat links allow anyone with the URL to view a snapshot of a chat. These links do
- not update when the original chat changes.
+ Public chats allow anyone with the URL to view a snapshot of a chat. They do not update
+ when the original chat changes.
diff --git a/surfsense_web/components/settings/general-settings-manager.tsx b/surfsense_web/components/settings/general-settings-manager.tsx
index 113d9d754..d0c08d881 100644
--- a/surfsense_web/components/settings/general-settings-manager.tsx
+++ b/surfsense_web/components/settings/general-settings-manager.tsx
@@ -208,10 +208,9 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager
-
Programmatic API access
+
API key access
- Allow personal access tokens to use this search space. Web and desktop sessions are not
- affected.
+ Allow API keys to access this search space.