From 895974878d7c23beae6624af8292f70178823937 Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Wed, 31 Dec 2025 16:44:29 -0300 Subject: [PATCH] refactor: move credentials to settings page and update sidebar --- .../src/app/dashboard/credentials/page.tsx | 245 ------------------ frontend/src/app/dashboard/settings/page.tsx | 188 ++++++++++---- frontend/src/components/sidebar.tsx | 8 +- 3 files changed, 148 insertions(+), 293 deletions(-) delete mode 100644 frontend/src/app/dashboard/credentials/page.tsx diff --git a/frontend/src/app/dashboard/credentials/page.tsx b/frontend/src/app/dashboard/credentials/page.tsx deleted file mode 100644 index 6bfd111..0000000 --- a/frontend/src/app/dashboard/credentials/page.tsx +++ /dev/null @@ -1,245 +0,0 @@ -"use client" - -import { useEffect, useState } from "react" -import { toast } from "sonner" -import { credentialsApi, ConfiguredService } from "@/lib/api" -import { Button } from "@/components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog" -import { Badge } from "@/components/ui/badge" -import { Check, Loader2, Plus, Shield, Trash2, X } from "lucide-react" - -export default function CredentialsPage() { - const [loading, setLoading] = useState(true) - const [services, setServices] = useState([]) - const [openDialog, setOpenDialog] = useState(false) - const [selectedService, setSelectedService] = useState("") - const [formData, setFormData] = useState>({}) - const [saving, setSaving] = useState(false) - - // Predefined schemas for known services - const schemas: Record = { - stripe: { - label: "Stripe", - fields: [ - { key: "secretKey", label: "Secret Key (sk_...)", type: "password" }, - { key: "webhookSecret", label: "Webhook Secret (whsec_...)", type: "password" }, - { key: "publishableKey", label: "Publishable Key (pk_...)", type: "text" }, - ] - }, - storage: { - label: "AWS S3 / Compatible", - fields: [ - { key: "endpoint", label: "Endpoint URL", type: "text" }, - { key: "region", label: "Region", type: "text" }, - { key: "bucket", label: "Bucket Name", type: "text" }, - { key: "accessKey", label: "Access Key ID", type: "text" }, - { key: "secretKey", label: "Secret Access Key", type: "password" }, - ] - }, - cpanel: { - label: "cPanel Integration", - fields: [ - { key: "host", label: "cPanel URL (https://domain:2083)", type: "text" }, - { key: "username", label: "Username", type: "text" }, - { key: "apiToken", label: "API Token", type: "password" }, - ] - }, - cloudflare_config: { - label: "Cloudflare", - fields: [ - { key: "apiToken", label: "API Token", type: "password" }, - { key: "zoneId", label: "Zone ID", type: "text" }, - ] - }, - smtp: { - label: "SMTP Email", - fields: [ - { key: "host", label: "Host", type: "text" }, - { key: "port", label: "Port", type: "number" }, - { key: "username", label: "Username", type: "text" }, - { key: "password", label: "Password", type: "password" }, - { key: "from_email", label: "From Email", type: "email" }, - { key: "from_name", label: "From Name", type: "text" }, - { key: "secure", label: "Use TLS", type: "checkbox" } // TODO handle checkbox - ] - }, - appwrite: { - label: "Appwrite", - fields: [ - { key: "endpoint", label: "Endpoint", type: "text" }, - { key: "projectId", label: "Project ID", type: "text" }, - { key: "apiKey", label: "API Key", type: "password" }, - ] - }, - firebase: { - label: "Firebase (JSON)", - fields: [ - { key: "serviceAccountJson", label: "Service Account JSON Content", type: "textarea" } - ] - } - } - - const availableServices = Object.keys(schemas) - - useEffect(() => { - loadServices() - }, []) - - const loadServices = async () => { - try { - setLoading(true) - const res = await credentialsApi.list() - // Backend returns { services: [...] } - if (res && res.services) { - setServices(res.services) - } - } catch (error) { - toast.error("Failed to load credentials") - console.error(error) - } finally { - setLoading(false) - } - } - - const handleEdit = (serviceName: string) => { - setSelectedService(serviceName) - setFormData({}) // Reset form, we don't load existing secrets for security - setOpenDialog(true) - } - - const handleSave = async () => { - if (!selectedService) return - - try { - setSaving(true) - await credentialsApi.save(selectedService, formData) - toast.success(`${schemas[selectedService]?.label || selectedService} credentials saved!`) - setOpenDialog(false) - loadServices() - } catch (error: any) { - toast.error(error.message || "Failed to save") - } finally { - setSaving(false) - } - } - - const handleDelete = async (serviceName: string) => { - if (!confirm(`Are you sure you want to delete credentials for ${serviceName}? This will break functionality relying on it.`)) return - - try { - await credentialsApi.delete(serviceName) - toast.success("Credentials deleted") - loadServices() - } catch (error: any) { - toast.error("Failed to delete") - } - } - - return ( -
-
-
-

System Credentials

-

- Manage external service connections securely. Keys are encrypted in the database. -

-
-
- -
- {services.map((svc) => ( - - - - {schemas[svc.service_name]?.label || svc.service_name} - - {svc.is_configured ? ( - - Active - - ) : ( - - Pending - - )} - - -
- -
-

- {svc.is_configured - ? `Last updated ${new Date(svc.updated_at).toLocaleDateString()}` - : "Not configured yet"} -

-
- - {svc.is_configured && ( - - )} -
-
-
- ))} -
- - - - - Configure {schemas[selectedService]?.label || selectedService} - - Enter the credentials for this service. They will be encrypted before storage. - - - -
- {schemas[selectedService]?.fields.map((field) => ( -
- - {field.type === 'textarea' ? ( -