mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-09 21:29:11 +00:00
Add a one-click action to export all conversations. Add a self-service delete account action to the settings page
This commit is contained in:
@@ -22,6 +22,10 @@ import {
|
|||||||
DropdownMenuRadioItem,
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import {
|
||||||
|
AlertDialog, AlertDialogAction, AlertDialogCancel,
|
||||||
|
AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -54,6 +58,8 @@ import {
|
|||||||
Brain,
|
Brain,
|
||||||
EyeSlash,
|
EyeSlash,
|
||||||
Eye,
|
Eye,
|
||||||
|
Download,
|
||||||
|
TrashSimple,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
|
|
||||||
import Loading from "../components/loading/loading";
|
import Loading from "../components/loading/loading";
|
||||||
@@ -63,6 +69,10 @@ import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/s
|
|||||||
import { AppSidebar } from "../components/appSidebar/appSidebar";
|
import { AppSidebar } from "../components/appSidebar/appSidebar";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { KhojLogoType } from "../components/logo/khojLogo";
|
import { KhojLogoType } from "../components/logo/khojLogo";
|
||||||
|
import { Progress } from "@/components/ui/progress";
|
||||||
|
|
||||||
|
import JSZip from "jszip";
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
interface DropdownComponentProps {
|
interface DropdownComponentProps {
|
||||||
items: ModelOptions[];
|
items: ModelOptions[];
|
||||||
@@ -296,6 +306,10 @@ export default function SettingsView() {
|
|||||||
const [numberValidationState, setNumberValidationState] = useState<PhoneNumberValidationState>(
|
const [numberValidationState, setNumberValidationState] = useState<PhoneNumberValidationState>(
|
||||||
PhoneNumberValidationState.Verified,
|
PhoneNumberValidationState.Verified,
|
||||||
);
|
);
|
||||||
|
const [isExporting, setIsExporting] = useState(false);
|
||||||
|
const [exportProgress, setExportProgress] = useState(0);
|
||||||
|
const [exportedConversations, setExportedConversations] = useState(0);
|
||||||
|
const [totalConversations, setTotalConversations] = useState(0);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const isMobileWidth = useIsMobileWidth();
|
const isMobileWidth = useIsMobileWidth();
|
||||||
|
|
||||||
@@ -311,8 +325,8 @@ export default function SettingsView() {
|
|||||||
initialUserConfig?.is_phone_number_verified
|
initialUserConfig?.is_phone_number_verified
|
||||||
? PhoneNumberValidationState.Verified
|
? PhoneNumberValidationState.Verified
|
||||||
: initialUserConfig?.phone_number
|
: initialUserConfig?.phone_number
|
||||||
? PhoneNumberValidationState.SendOTP
|
? PhoneNumberValidationState.SendOTP
|
||||||
: PhoneNumberValidationState.Setup,
|
: PhoneNumberValidationState.Setup,
|
||||||
);
|
);
|
||||||
setName(initialUserConfig?.given_name);
|
setName(initialUserConfig?.given_name);
|
||||||
setNotionToken(initialUserConfig?.notion_token ?? null);
|
setNotionToken(initialUserConfig?.notion_token ?? null);
|
||||||
@@ -538,6 +552,56 @@ export default function SettingsView() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const exportChats = async () => {
|
||||||
|
try {
|
||||||
|
setIsExporting(true);
|
||||||
|
|
||||||
|
// Get total conversation count
|
||||||
|
const statsResponse = await fetch('/api/chat/stats');
|
||||||
|
const stats = await statsResponse.json();
|
||||||
|
const total = stats.num_conversations;
|
||||||
|
setTotalConversations(total);
|
||||||
|
|
||||||
|
// Create zip file
|
||||||
|
const zip = new JSZip();
|
||||||
|
const conversations = [];
|
||||||
|
|
||||||
|
// Fetch all conversations in batches of 10
|
||||||
|
for (let page = 0; page * 10 < total; page++) {
|
||||||
|
const response = await fetch(`/api/chat/export?page=${page}`);
|
||||||
|
const data = await response.json();
|
||||||
|
conversations.push(...data);
|
||||||
|
|
||||||
|
setExportedConversations((page + 1) * 10);
|
||||||
|
setExportProgress(((page + 1) * 10 / total) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add conversations to zip
|
||||||
|
zip.file("conversations.json", JSON.stringify(conversations, null, 2));
|
||||||
|
|
||||||
|
// Generate and download zip
|
||||||
|
const content = await zip.generateAsync({ type: "blob" });
|
||||||
|
saveAs(content, "khoj-conversations.zip");
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Export Complete",
|
||||||
|
description: `Successfully exported ${conversations.length} conversations`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error exporting chats:", error);
|
||||||
|
toast({
|
||||||
|
title: "Export Failed",
|
||||||
|
description: "Failed to export chats. Please try again.",
|
||||||
|
variant: "destructive"
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsExporting(false);
|
||||||
|
setExportProgress(0);
|
||||||
|
setExportedConversations(0);
|
||||||
|
setTotalConversations(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const saveNotionToken = async () => {
|
const saveNotionToken = async () => {
|
||||||
if (!notionToken) return;
|
if (!notionToken) return;
|
||||||
// Save Notion API key to server
|
// Save Notion API key to server
|
||||||
@@ -731,93 +795,93 @@ export default function SettingsView() {
|
|||||||
)) ||
|
)) ||
|
||||||
(userConfig.subscription_state ===
|
(userConfig.subscription_state ===
|
||||||
"subscribed" && (
|
"subscribed" && (
|
||||||
<>
|
<>
|
||||||
<p className="text-xl text-primary/80">
|
<p className="text-xl text-primary/80">
|
||||||
Futurist
|
Futurist
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-400">
|
|
||||||
Subscription <b>renews</b> on{" "}
|
|
||||||
<b>
|
|
||||||
{
|
|
||||||
userConfig.subscription_renewal_date
|
|
||||||
}
|
|
||||||
</b>
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)) ||
|
|
||||||
(userConfig.subscription_state ===
|
|
||||||
"unsubscribed" && (
|
|
||||||
<>
|
|
||||||
<p className="text-xl">Futurist</p>
|
|
||||||
<p className="text-gray-400">
|
|
||||||
Subscription <b>ends</b> on{" "}
|
|
||||||
<b>
|
|
||||||
{
|
|
||||||
userConfig.subscription_renewal_date
|
|
||||||
}
|
|
||||||
</b>
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)) ||
|
|
||||||
(userConfig.subscription_state ===
|
|
||||||
"expired" && (
|
|
||||||
<>
|
|
||||||
<p className="text-xl">Humanist</p>
|
|
||||||
{(userConfig.subscription_renewal_date && (
|
|
||||||
<p className="text-gray-400">
|
<p className="text-gray-400">
|
||||||
Subscription <b>expired</b>{" "}
|
Subscription <b>renews</b> on{" "}
|
||||||
on{" "}
|
|
||||||
<b>
|
<b>
|
||||||
{
|
{
|
||||||
userConfig.subscription_renewal_date
|
userConfig.subscription_renewal_date
|
||||||
}
|
}
|
||||||
</b>
|
</b>
|
||||||
</p>
|
</p>
|
||||||
)) || (
|
</>
|
||||||
|
)) ||
|
||||||
|
(userConfig.subscription_state ===
|
||||||
|
"unsubscribed" && (
|
||||||
|
<>
|
||||||
|
<p className="text-xl">Futurist</p>
|
||||||
<p className="text-gray-400">
|
<p className="text-gray-400">
|
||||||
Check{" "}
|
Subscription <b>ends</b> on{" "}
|
||||||
<a
|
<b>
|
||||||
href="https://khoj.dev/#pricing"
|
{
|
||||||
target="_blank"
|
userConfig.subscription_renewal_date
|
||||||
>
|
}
|
||||||
pricing page
|
</b>
|
||||||
</a>{" "}
|
|
||||||
to compare plans.
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
</>
|
||||||
</>
|
)) ||
|
||||||
))}
|
(userConfig.subscription_state ===
|
||||||
|
"expired" && (
|
||||||
|
<>
|
||||||
|
<p className="text-xl">Humanist</p>
|
||||||
|
{(userConfig.subscription_renewal_date && (
|
||||||
|
<p className="text-gray-400">
|
||||||
|
Subscription <b>expired</b>{" "}
|
||||||
|
on{" "}
|
||||||
|
<b>
|
||||||
|
{
|
||||||
|
userConfig.subscription_renewal_date
|
||||||
|
}
|
||||||
|
</b>
|
||||||
|
</p>
|
||||||
|
)) || (
|
||||||
|
<p className="text-gray-400">
|
||||||
|
Check{" "}
|
||||||
|
<a
|
||||||
|
href="https://khoj.dev/#pricing"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
pricing page
|
||||||
|
</a>{" "}
|
||||||
|
to compare plans.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
{(userConfig.subscription_state ==
|
{(userConfig.subscription_state ==
|
||||||
"subscribed" && (
|
"subscribed" && (
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="hover:text-red-400"
|
|
||||||
onClick={() =>
|
|
||||||
setSubscription("cancel")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ArrowCircleDown className="h-5 w-5 mr-2" />
|
|
||||||
Unsubscribe
|
|
||||||
</Button>
|
|
||||||
)) ||
|
|
||||||
(userConfig.subscription_state ==
|
|
||||||
"unsubscribed" && (
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="text-primary/80 hover:text-primary"
|
className="hover:text-red-400"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setSubscription("resubscribe")
|
setSubscription("cancel")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ArrowCircleUp
|
<ArrowCircleDown className="h-5 w-5 mr-2" />
|
||||||
weight="bold"
|
Unsubscribe
|
||||||
className="h-5 w-5 mr-2"
|
|
||||||
/>
|
|
||||||
Resubscribe
|
|
||||||
</Button>
|
</Button>
|
||||||
)) ||
|
)) ||
|
||||||
|
(userConfig.subscription_state ==
|
||||||
|
"unsubscribed" && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="text-primary/80 hover:text-primary"
|
||||||
|
onClick={() =>
|
||||||
|
setSubscription("resubscribe")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ArrowCircleUp
|
||||||
|
weight="bold"
|
||||||
|
className="h-5 w-5 mr-2"
|
||||||
|
/>
|
||||||
|
Resubscribe
|
||||||
|
</Button>
|
||||||
|
)) ||
|
||||||
(userConfig.subscription_enabled_trial_at && (
|
(userConfig.subscription_enabled_trial_at && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -907,16 +971,16 @@ export default function SettingsView() {
|
|||||||
<Button variant="outline" size="sm">
|
<Button variant="outline" size="sm">
|
||||||
{(userConfig.enabled_content_source
|
{(userConfig.enabled_content_source
|
||||||
.github && (
|
.github && (
|
||||||
<>
|
<>
|
||||||
<Files className="h-5 w-5 inline mr-1" />
|
<Files className="h-5 w-5 inline mr-1" />
|
||||||
Manage
|
Manage
|
||||||
</>
|
</>
|
||||||
)) || (
|
)) || (
|
||||||
<>
|
<>
|
||||||
<Plugs className="h-5 w-5 inline mr-1" />
|
<Plugs className="h-5 w-5 inline mr-1" />
|
||||||
Connect
|
Connect
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -958,8 +1022,8 @@ export default function SettingsView() {
|
|||||||
{
|
{
|
||||||
/* Show connect to notion button if notion oauth url setup and user disconnected*/
|
/* Show connect to notion button if notion oauth url setup and user disconnected*/
|
||||||
userConfig.notion_oauth_url &&
|
userConfig.notion_oauth_url &&
|
||||||
!userConfig.enabled_content_source
|
!userConfig.enabled_content_source
|
||||||
.notion ? (
|
.notion ? (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -973,39 +1037,39 @@ export default function SettingsView() {
|
|||||||
Connect
|
Connect
|
||||||
</Button>
|
</Button>
|
||||||
) : /* Show sync button if user connected to notion and API key unchanged */
|
) : /* Show sync button if user connected to notion and API key unchanged */
|
||||||
userConfig.enabled_content_source.notion &&
|
userConfig.enabled_content_source.notion &&
|
||||||
notionToken ===
|
notionToken ===
|
||||||
userConfig.notion_token ? (
|
userConfig.notion_token ? (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
syncContent("notion")
|
syncContent("notion")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ArrowsClockwise className="h-5 w-5 inline mr-1" />
|
<ArrowsClockwise className="h-5 w-5 inline mr-1" />
|
||||||
Sync
|
Sync
|
||||||
</Button>
|
</Button>
|
||||||
) : /* Show set API key button notion oauth url not set setup */
|
) : /* Show set API key button notion oauth url not set setup */
|
||||||
!userConfig.notion_oauth_url ? (
|
!userConfig.notion_oauth_url ? (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={saveNotionToken}
|
onClick={saveNotionToken}
|
||||||
disabled={
|
disabled={
|
||||||
notionToken ===
|
notionToken ===
|
||||||
userConfig.notion_token
|
userConfig.notion_token
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FloppyDisk className="h-5 w-5 inline mr-1" />
|
<FloppyDisk className="h-5 w-5 inline mr-1" />
|
||||||
{(userConfig.enabled_content_source
|
{(userConfig.enabled_content_source
|
||||||
.notion &&
|
.notion &&
|
||||||
"Update API Key") ||
|
"Update API Key") ||
|
||||||
"Set API Key"}
|
"Set API Key"}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -1121,18 +1185,18 @@ export default function SettingsView() {
|
|||||||
Chat on Whatsapp
|
Chat on Whatsapp
|
||||||
{(numberValidationState ===
|
{(numberValidationState ===
|
||||||
PhoneNumberValidationState.Verified && (
|
PhoneNumberValidationState.Verified && (
|
||||||
<CheckCircle
|
<CheckCircle
|
||||||
weight="bold"
|
weight="bold"
|
||||||
className="h-4 w-4 ml-1 text-green-400"
|
className="h-4 w-4 ml-1 text-green-400"
|
||||||
/>
|
/>
|
||||||
)) ||
|
)) ||
|
||||||
(numberValidationState !==
|
(numberValidationState !==
|
||||||
PhoneNumberValidationState.Setup && (
|
PhoneNumberValidationState.Setup && (
|
||||||
<ExclamationMark
|
<ExclamationMark
|
||||||
weight="bold"
|
weight="bold"
|
||||||
className="h-4 w-4 ml-1 text-yellow-400"
|
className="h-4 w-4 ml-1 text-yellow-400"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-4">
|
<CardContent className="grid gap-4">
|
||||||
<p className="text-gray-400">
|
<p className="text-gray-400">
|
||||||
@@ -1161,90 +1225,192 @@ export default function SettingsView() {
|
|||||||
/>
|
/>
|
||||||
{numberValidationState ===
|
{numberValidationState ===
|
||||||
PhoneNumberValidationState.VerifyOTP && (
|
PhoneNumberValidationState.VerifyOTP && (
|
||||||
<>
|
<>
|
||||||
<p>{`Enter the OTP sent to your number: ${phoneNumber}`}</p>
|
<p>{`Enter the OTP sent to your number: ${phoneNumber}`}</p>
|
||||||
<InputOTP
|
<InputOTP
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
maxLength={6}
|
maxLength={6}
|
||||||
value={otp || ""}
|
value={otp || ""}
|
||||||
onChange={setOTP}
|
onChange={setOTP}
|
||||||
onComplete={() =>
|
onComplete={() =>
|
||||||
setNumberValidationState(
|
setNumberValidationState(
|
||||||
PhoneNumberValidationState.VerifyOTP,
|
PhoneNumberValidationState.VerifyOTP,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<InputOTPGroup>
|
<InputOTPGroup>
|
||||||
<InputOTPSlot index={0} />
|
<InputOTPSlot index={0} />
|
||||||
<InputOTPSlot index={1} />
|
<InputOTPSlot index={1} />
|
||||||
<InputOTPSlot index={2} />
|
<InputOTPSlot index={2} />
|
||||||
<InputOTPSlot index={3} />
|
<InputOTPSlot index={3} />
|
||||||
<InputOTPSlot index={4} />
|
<InputOTPSlot index={4} />
|
||||||
<InputOTPSlot index={5} />
|
<InputOTPSlot index={5} />
|
||||||
</InputOTPGroup>
|
</InputOTPGroup>
|
||||||
</InputOTP>
|
</InputOTP>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
{(numberValidationState ===
|
{(numberValidationState ===
|
||||||
PhoneNumberValidationState.VerifyOTP && (
|
PhoneNumberValidationState.VerifyOTP && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={verifyOTP}
|
onClick={verifyOTP}
|
||||||
>
|
>
|
||||||
Verify
|
Verify
|
||||||
</Button>
|
</Button>
|
||||||
)) || (
|
)) || (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={
|
disabled={
|
||||||
!phoneNumber ||
|
!phoneNumber ||
|
||||||
(phoneNumber ===
|
(phoneNumber ===
|
||||||
userConfig.phone_number &&
|
userConfig.phone_number &&
|
||||||
numberValidationState ===
|
numberValidationState ===
|
||||||
PhoneNumberValidationState.Verified) ||
|
PhoneNumberValidationState.Verified) ||
|
||||||
!isValidPhoneNumber(phoneNumber)
|
!isValidPhoneNumber(phoneNumber)
|
||||||
}
|
}
|
||||||
onClick={sendOTP}
|
onClick={sendOTP}
|
||||||
>
|
>
|
||||||
{!userConfig.phone_number ? (
|
{!userConfig.phone_number ? (
|
||||||
<>
|
<>
|
||||||
<Plugs className="inline mr-2" />
|
<Plugs className="inline mr-2" />
|
||||||
Setup Whatsapp
|
Setup Whatsapp
|
||||||
</>
|
</>
|
||||||
) : !phoneNumber ||
|
) : !phoneNumber ||
|
||||||
(phoneNumber ===
|
(phoneNumber ===
|
||||||
userConfig.phone_number &&
|
userConfig.phone_number &&
|
||||||
numberValidationState ===
|
numberValidationState ===
|
||||||
PhoneNumberValidationState.Verified) ||
|
PhoneNumberValidationState.Verified) ||
|
||||||
!isValidPhoneNumber(phoneNumber) ? (
|
!isValidPhoneNumber(phoneNumber) ? (
|
||||||
<>
|
<>
|
||||||
<PlugsConnected className="inline mr-2 text-green-400" />
|
<PlugsConnected className="inline mr-2 text-green-400" />
|
||||||
Switch Number
|
Switch Number
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
Send OTP{" "}
|
Send OTP{" "}
|
||||||
<ArrowRight
|
<ArrowRight
|
||||||
className="inline ml-2"
|
className="inline ml-2"
|
||||||
weight="bold"
|
weight="bold"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{numberValidationState ===
|
{numberValidationState ===
|
||||||
PhoneNumberValidationState.Verified && (
|
PhoneNumberValidationState.Verified && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => disconnectNumber()}
|
onClick={() => disconnectNumber()}
|
||||||
>
|
>
|
||||||
<CloudSlash className="h-5 w-5 mr-2" />
|
<CloudSlash className="h-5 w-5 mr-2" />
|
||||||
Disconnect
|
Disconnect
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="section grid gap-8">
|
||||||
|
<div id="clients" className="text-2xl">
|
||||||
|
Account
|
||||||
|
</div>
|
||||||
|
<div className="cards flex flex-wrap gap-16">
|
||||||
|
<Card className={cardClassName}>
|
||||||
|
<CardHeader className="text-xl flex flex-row">
|
||||||
|
<Download className="h-7 w-7 mr-2" />
|
||||||
|
Export Data
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="overflow-hidden">
|
||||||
|
<p className="pb-4 text-gray-400">
|
||||||
|
Download all your chat conversations
|
||||||
|
</p>
|
||||||
|
{exportProgress > 0 && (
|
||||||
|
<div className="w-full mt-4">
|
||||||
|
<Progress value={exportProgress} className="w-full" />
|
||||||
|
<p className="text-sm text-gray-500 mt-2">
|
||||||
|
Exported {exportedConversations} of {totalConversations} conversations
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={exportChats}
|
||||||
|
disabled={isExporting}
|
||||||
|
>
|
||||||
|
<Download className="h-5 w-5 mr-2" />
|
||||||
|
{isExporting ? "Exporting..." : "Export Chats"}
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className={cardClassName}>
|
||||||
|
<CardHeader className="text-xl flex flex-row">
|
||||||
|
<TrashSimple className="h-7 w-7 mr-2 text-red-500" />
|
||||||
|
Delete Account
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="overflow-hidden">
|
||||||
|
<p className="pb-4 text-gray-400">
|
||||||
|
This will delete all your account data, including conversations, agents, and any assets you{"'"}ve generated. Be sure to export before you do this if you want to keep your information.
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
|
<AlertDialog>
|
||||||
|
<AlertDialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="text-red-500 hover:text-red-600 hover:bg-red-50"
|
||||||
|
>
|
||||||
|
<TrashSimple className="h-5 w-5 mr-2" />
|
||||||
|
Delete Account
|
||||||
|
</Button>
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
This action is irreversible. This will permanently delete your account
|
||||||
|
and remove all your data from our servers.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
className="bg-red-500 hover:bg-red-600"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/self', {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Failed to delete account');
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Account Deleted",
|
||||||
|
description: "Your account has been successfully deleted.",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Redirect to home page after successful deletion
|
||||||
|
window.location.href = "/";
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting account:', error);
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Failed to delete account. Please try again or contact support.",
|
||||||
|
variant: "destructive"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TrashSimple className="h-5 w-5 mr-2" />
|
||||||
|
Delete Account
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"@radix-ui/react-toggle": "^1.1.0",
|
"@radix-ui/react-toggle": "^1.1.0",
|
||||||
"@radix-ui/react-tooltip": "^1.1.6",
|
"@radix-ui/react-tooltip": "^1.1.6",
|
||||||
"@radix-ui/themes": "^3.1.1",
|
"@radix-ui/themes": "^3.1.1",
|
||||||
|
"@types/file-saver": "^2.0.7",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@@ -53,9 +54,11 @@
|
|||||||
"embla-carousel-react": "^8.5.1",
|
"embla-carousel-react": "^8.5.1",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.2.3",
|
"eslint-config-next": "14.2.3",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"framer-motion": "^12.0.6",
|
"framer-motion": "^12.0.6",
|
||||||
"input-otp": "^1.2.4",
|
"input-otp": "^1.2.4",
|
||||||
"intl-tel-input": "^23.8.1",
|
"intl-tel-input": "^23.8.1",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.21",
|
||||||
"libphonenumber-js": "^1.11.4",
|
"libphonenumber-js": "^1.11.4",
|
||||||
"lucide-react": "^0.468.0",
|
"lucide-react": "^0.468.0",
|
||||||
|
|||||||
@@ -1185,6 +1185,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
dompurify "*"
|
dompurify "*"
|
||||||
|
|
||||||
|
"@types/file-saver@^2.0.7":
|
||||||
|
version "2.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.7.tgz#8dbb2f24bdc7486c54aa854eb414940bbd056f7d"
|
||||||
|
integrity sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==
|
||||||
|
|
||||||
"@types/geojson@*":
|
"@types/geojson@*":
|
||||||
version "7946.0.16"
|
version "7946.0.16"
|
||||||
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
|
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
|
||||||
@@ -1770,6 +1775,11 @@ confbox@^0.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.1.tgz#ae39f2c99699afa451d00206479f15f9a1208a8b"
|
resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.1.tgz#ae39f2c99699afa451d00206479f15f9a1208a8b"
|
||||||
integrity sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==
|
integrity sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==
|
||||||
|
|
||||||
|
core-util-is@~1.0.0:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||||
|
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
|
||||||
|
|
||||||
cose-base@^1.0.0:
|
cose-base@^1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a"
|
resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a"
|
||||||
@@ -2739,6 +2749,11 @@ file-entry-cache@^6.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flat-cache "^3.0.4"
|
flat-cache "^3.0.4"
|
||||||
|
|
||||||
|
file-saver@^2.0.5:
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
|
||||||
|
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
|
||||||
|
|
||||||
fill-range@^7.1.1:
|
fill-range@^7.1.1:
|
||||||
version "7.1.1"
|
version "7.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||||
@@ -3065,6 +3080,11 @@ ignore@^5.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||||
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
||||||
|
|
||||||
|
immediate@~3.0.5:
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||||
|
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
||||||
|
|
||||||
import-fresh@^3.2.1:
|
import-fresh@^3.2.1:
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
|
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
|
||||||
@@ -3086,7 +3106,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2:
|
inherits@2, inherits@~2.0.3:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@@ -3343,6 +3363,11 @@ isarray@^2.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
||||||
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
|
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
|
||||||
|
|
||||||
|
isarray@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
|
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
|
||||||
|
|
||||||
isexe@^2.0.0:
|
isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
@@ -3427,6 +3452,16 @@ json5@^1.0.2:
|
|||||||
object.assign "^4.1.4"
|
object.assign "^4.1.4"
|
||||||
object.values "^1.1.6"
|
object.values "^1.1.6"
|
||||||
|
|
||||||
|
jszip@^3.10.1:
|
||||||
|
version "3.10.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
|
||||||
|
integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
|
||||||
|
dependencies:
|
||||||
|
lie "~3.3.0"
|
||||||
|
pako "~1.0.2"
|
||||||
|
readable-stream "~2.3.6"
|
||||||
|
setimmediate "^1.0.5"
|
||||||
|
|
||||||
katex@^0.16.21, katex@^0.16.9:
|
katex@^0.16.21, katex@^0.16.9:
|
||||||
version "0.16.21"
|
version "0.16.21"
|
||||||
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.21.tgz#8f63c659e931b210139691f2cc7bb35166b792a3"
|
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.21.tgz#8f63c659e931b210139691f2cc7bb35166b792a3"
|
||||||
@@ -3497,6 +3532,13 @@ libphonenumber-js@^1.11.4:
|
|||||||
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.12.5.tgz#8e6043a67112d4beedb8627b359a613f04d88fba"
|
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.12.5.tgz#8e6043a67112d4beedb8627b359a613f04d88fba"
|
||||||
integrity sha512-DOjiaVjjSmap12ztyb4QgoFmUe/GbgnEXHu+R7iowk0lzDIjScvPAm8cK9RYTEobbRb0OPlwlZUGTTJPJg13Kw==
|
integrity sha512-DOjiaVjjSmap12ztyb4QgoFmUe/GbgnEXHu+R7iowk0lzDIjScvPAm8cK9RYTEobbRb0OPlwlZUGTTJPJg13Kw==
|
||||||
|
|
||||||
|
lie@~3.3.0:
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
|
||||||
|
integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
|
||||||
|
dependencies:
|
||||||
|
immediate "~3.0.5"
|
||||||
|
|
||||||
lilconfig@^3.0.0, lilconfig@^3.1.3:
|
lilconfig@^3.0.0, lilconfig@^3.1.3:
|
||||||
version "3.1.3"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4"
|
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4"
|
||||||
@@ -3960,6 +4002,11 @@ package-manager-detector@^0.2.8:
|
|||||||
dependencies:
|
dependencies:
|
||||||
quansync "^0.2.7"
|
quansync "^0.2.7"
|
||||||
|
|
||||||
|
pako@~1.0.2:
|
||||||
|
version "1.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||||
|
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||||
|
|
||||||
parent-module@^1.0.0:
|
parent-module@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||||
@@ -4160,6 +4207,11 @@ prettier@3.3.3:
|
|||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
|
||||||
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
|
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
|
||||||
|
|
||||||
|
process-nextick-args@~2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
|
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||||
|
|
||||||
prop-types@^15.8.1:
|
prop-types@^15.8.1:
|
||||||
version "15.8.1"
|
version "15.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||||
@@ -4309,6 +4361,19 @@ read-cache@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
pify "^2.3.0"
|
pify "^2.3.0"
|
||||||
|
|
||||||
|
readable-stream@~2.3.6:
|
||||||
|
version "2.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
||||||
|
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
||||||
|
dependencies:
|
||||||
|
core-util-is "~1.0.0"
|
||||||
|
inherits "~2.0.3"
|
||||||
|
isarray "~1.0.0"
|
||||||
|
process-nextick-args "~2.0.0"
|
||||||
|
safe-buffer "~5.1.1"
|
||||||
|
string_decoder "~1.1.1"
|
||||||
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
readdirp@~3.6.0:
|
readdirp@~3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||||
@@ -4433,6 +4498,11 @@ safe-array-concat@^1.1.3:
|
|||||||
has-symbols "^1.1.0"
|
has-symbols "^1.1.0"
|
||||||
isarray "^2.0.5"
|
isarray "^2.0.5"
|
||||||
|
|
||||||
|
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||||
|
version "5.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
|
||||||
safe-push-apply@^1.0.0:
|
safe-push-apply@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5"
|
resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5"
|
||||||
@@ -4503,6 +4573,11 @@ set-proto@^1.0.0:
|
|||||||
es-errors "^1.3.0"
|
es-errors "^1.3.0"
|
||||||
es-object-atoms "^1.0.0"
|
es-object-atoms "^1.0.0"
|
||||||
|
|
||||||
|
setimmediate@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||||
|
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
|
||||||
|
|
||||||
shadcn-ui@^0.9.0:
|
shadcn-ui@^0.9.0:
|
||||||
version "0.9.5"
|
version "0.9.5"
|
||||||
resolved "https://registry.yarnpkg.com/shadcn-ui/-/shadcn-ui-0.9.5.tgz#b7f35b78f2c7fe0b71651fe542ed62f01795b4a6"
|
resolved "https://registry.yarnpkg.com/shadcn-ui/-/shadcn-ui-0.9.5.tgz#b7f35b78f2c7fe0b71651fe542ed62f01795b4a6"
|
||||||
@@ -4616,6 +4691,7 @@ string-argv@^0.3.2:
|
|||||||
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
|
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
|
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
|
||||||
|
name string-width-cjs
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@@ -4710,6 +4786,13 @@ string.prototype.trimstart@^1.0.8:
|
|||||||
define-properties "^1.2.1"
|
define-properties "^1.2.1"
|
||||||
es-object-atoms "^1.0.0"
|
es-object-atoms "^1.0.0"
|
||||||
|
|
||||||
|
string_decoder@~1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||||
|
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
@@ -5043,7 +5126,7 @@ use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc"
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc"
|
||||||
integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==
|
integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==
|
||||||
|
|
||||||
util-deprecate@^1.0.2:
|
util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||||
|
|||||||
@@ -939,6 +939,28 @@ class ConversationAdapters:
|
|||||||
|
|
||||||
return conversation
|
return conversation
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@require_valid_user
|
||||||
|
def get_all_conversations_for_export(user: KhojUser, page: Optional[int] = 0):
|
||||||
|
all_conversations = Conversation.objects.filter(user=user).prefetch_related("agent")[page : page + 10]
|
||||||
|
histories = []
|
||||||
|
for conversation in all_conversations:
|
||||||
|
history = {
|
||||||
|
"title": conversation.title,
|
||||||
|
"agent": conversation.agent.name if conversation.agent else "Khoj",
|
||||||
|
"created_at": datetime.strftime(conversation.created_at, "%Y-%m-%d %H:%M:%S"),
|
||||||
|
"updated_at": datetime.strftime(conversation.updated_at, "%Y-%m-%d %H:%M:%S"),
|
||||||
|
"conversation_log": conversation.conversation_log,
|
||||||
|
"file_filters": conversation.file_filters,
|
||||||
|
}
|
||||||
|
histories.append(history)
|
||||||
|
return histories
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@require_valid_user
|
||||||
|
def get_num_conversations(user: KhojUser):
|
||||||
|
return Conversation.objects.filter(user=user).count()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@require_valid_user
|
@require_valid_user
|
||||||
def get_conversation_sessions(user: KhojUser, client_application: ClientApplication = None):
|
def get_conversation_sessions(user: KhojUser, client_application: ClientApplication = None):
|
||||||
|
|||||||
@@ -1361,6 +1361,7 @@ help_message = PromptTemplate.from_template(
|
|||||||
- **/default**: Chat using your knowledge base and Khoj's general knowledge for context.
|
- **/default**: Chat using your knowledge base and Khoj's general knowledge for context.
|
||||||
- **/online**: Chat using the internet as a source of information.
|
- **/online**: Chat using the internet as a source of information.
|
||||||
- **/image**: Generate an image based on your message.
|
- **/image**: Generate an image based on your message.
|
||||||
|
- **/research**: Go deeper in a topic for more accurate, in-depth responses.
|
||||||
- **/help**: Show this help message.
|
- **/help**: Show this help message.
|
||||||
|
|
||||||
You are using the **{model}** model on the **{device}**.
|
You are using the **{model}** model on the **{device}**.
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ conversation_command_rate_limiter = ConversationCommandRateLimiter(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@api.delete("/self")
|
||||||
|
@requires(["authenticated"])
|
||||||
|
def delete_self(request: Request):
|
||||||
|
user = request.user.object
|
||||||
|
user.delete()
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
|
||||||
@api.get("/search", response_model=List[SearchResponse])
|
@api.get("/search", response_model=List[SearchResponse])
|
||||||
@requires(["authenticated"])
|
@requires(["authenticated"])
|
||||||
async def search(
|
async def search(
|
||||||
|
|||||||
@@ -94,6 +94,22 @@ conversation_command_rate_limiter = ConversationCommandRateLimiter(
|
|||||||
api_chat = APIRouter()
|
api_chat = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@api_chat.get("/stats", response_class=Response)
|
||||||
|
@requires(["authenticated"])
|
||||||
|
def chat_stats(request: Request, common: CommonQueryParams) -> Response:
|
||||||
|
num_conversations = ConversationAdapters.get_num_conversations(request.user.object)
|
||||||
|
return Response(
|
||||||
|
content=json.dumps({"num_conversations": num_conversations}), media_type="application/json", status_code=200
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@api_chat.get("/export", response_class=Response)
|
||||||
|
@requires(["authenticated"])
|
||||||
|
def export_conversation(request: Request, common: CommonQueryParams, page: Optional[int] = 1) -> Response:
|
||||||
|
all_conversations = ConversationAdapters.get_all_conversations_for_export(request.user.object, page=page)
|
||||||
|
return Response(content=json.dumps(all_conversations), media_type="application/json", status_code=200)
|
||||||
|
|
||||||
|
|
||||||
@api_chat.get("/conversation/file-filters/{conversation_id}", response_class=Response)
|
@api_chat.get("/conversation/file-filters/{conversation_id}", response_class=Response)
|
||||||
@requires(["authenticated"])
|
@requires(["authenticated"])
|
||||||
def get_file_filter(request: Request, conversation_id: str) -> Response:
|
def get_file_filter(request: Request, conversation_id: str) -> Response:
|
||||||
|
|||||||
Reference in New Issue
Block a user