"use client"; import { GraphQLClient, gql } from "graphql-request"; import Image from "next/image"; import { useEffect, useState } from "react"; const GET_CONNECTORS_QUERY = gql` query GetConnectors { connectors { success message connectors { id type enabled config metadata { id target platform name description logo logoDark } createdAt updatedAt } availableConnectors { id target platform name description logo logoDark configTemplate formItems { key type label placeholder required defaultValue description } isAdded } } } `; const CREATE_CONNECTOR_MUTATION = gql` mutation CreateConnector($input: CreateConnectorInput!) { createConnector(input: $input) { success message connector { id type enabled metadata { name platform } } } } `; const UPDATE_CONNECTOR_MUTATION = gql` mutation UpdateConnector($input: UpdateConnectorInput!) { updateConnector(input: $input) { success message connector { id enabled } } } `; const DELETE_CONNECTOR_MUTATION = gql` mutation DeleteConnector($id: ID!) { deleteConnector(id: $id) { success message } } `; const TOGGLE_CONNECTOR_MUTATION = gql` mutation ToggleConnector($id: ID!, $enabled: Boolean!) { toggleConnector(id: $id, enabled: $enabled) { success message connector { id enabled } } } `; const TEST_CONNECTOR_MUTATION = gql` mutation TestConnector($id: ID!) { testConnector(id: $id) { success message } } `; interface Connector { id: string; type: string; enabled: boolean; config: Record; metadata: { id: string; target: string; platform?: string; name: string | Record; description: string | Record; logo: string; logoDark: string | null; }; createdAt: string; updatedAt: string; } interface FactoryConnector { id: string; target: string; platform?: string; name: string | Record; description: string | Record; logo: string; logoDark: string | null; configTemplate: Record | null; formItems: FormItem[]; isAdded: boolean; } interface FormItem { key: string; type: string; label: string | Record; placeholder?: string | Record; required?: boolean; defaultValue?: string | boolean | number | string[]; description?: string | Record; } interface ConnectorsResponse { connectors: { success: boolean; message?: string; connectors?: Connector[]; availableConnectors?: FactoryConnector[]; }; } export default function ConnectorsPage() { const [connectors, setConnectors] = useState([]); const [availableConnectors, setAvailableConnectors] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [selectedFactory, setSelectedFactory] = useState(null); const [showConfig, setShowConfig] = useState(false); const [configForm, setConfigForm] = useState>({}); useEffect(() => { fetchConnectors(); }, []); const fetchConnectors = async () => { setLoading(true); setError(null); try { const storedToken = localStorage.getItem("accessToken"); if (!storedToken) { setError("Aucun token trouvé. Veuillez vous connecter."); return; } const authenticatedClient = new GraphQLClient(process.env.NEXT_PUBLIC_APP_URL + "/api/graphql", { headers: { Authorization: `Bearer ${storedToken}`, }, }); const response = await authenticatedClient.request(GET_CONNECTORS_QUERY); if (response.connectors.success) { setConnectors(response.connectors.connectors || []); setAvailableConnectors(response.connectors.availableConnectors || []); } else { setError(response.connectors.message || "Erreur lors du chargement"); } } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : "Une erreur est survenue"; setError(errorMessage); console.error("Erreur lors du chargement des connecteurs:", err); } finally { setLoading(false); } }; const handleCreateConnector = async () => { if (!selectedFactory) return; try { const storedToken = localStorage.getItem("accessToken"); if (!storedToken) { setError("Aucun token trouvé. Veuillez vous connecter."); return; } const authenticatedClient = new GraphQLClient(process.env.NEXT_PUBLIC_APP_URL + "/api/graphql", { headers: { Authorization: `Bearer ${storedToken}`, }, }); const response = (await authenticatedClient.request(CREATE_CONNECTOR_MUTATION, { input: { connectorId: selectedFactory.id, config: configForm, }, })) as { createConnector: { success: boolean; message?: string; connector?: { id: string; enabled: boolean; metadata: { name: string | Record; platform?: string; }; }; }; }; if (response.createConnector.success) { await fetchConnectors(); setShowConfig(false); setSelectedFactory(null); setConfigForm({}); } else { setError(response.createConnector.message || "Erreur lors de la création"); } } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : "Une erreur est survenue"; setError(errorMessage); } }; const handleToggleConnector = async (id: string, enabled: boolean) => { try { const storedToken = localStorage.getItem("accessToken"); if (!storedToken) return; const authenticatedClient = new GraphQLClient(process.env.NEXT_PUBLIC_APP_URL + "/api/graphql", { headers: { Authorization: `Bearer ${storedToken}`, }, }); await authenticatedClient.request(TOGGLE_CONNECTOR_MUTATION, { id, enabled, }); await fetchConnectors(); } catch (err: unknown) { console.error("Erreur lors du basculement:", err); } }; const handleDeleteConnector = async (id: string) => { if (!confirm("Êtes-vous sûr de vouloir supprimer ce connecteur ?")) return; try { const storedToken = localStorage.getItem("accessToken"); if (!storedToken) return; const authenticatedClient = new GraphQLClient(process.env.NEXT_PUBLIC_APP_URL + "/api/graphql", { headers: { Authorization: `Bearer ${storedToken}`, }, }); await authenticatedClient.request(DELETE_CONNECTOR_MUTATION, { id }); await fetchConnectors(); } catch (err: unknown) { console.error("Erreur lors de la suppression:", err); } }; const handleTestConnector = async (id: string) => { try { const storedToken = localStorage.getItem("accessToken"); if (!storedToken) return; const authenticatedClient = new GraphQLClient(process.env.NEXT_PUBLIC_APP_URL + "/api/graphql", { headers: { Authorization: `Bearer ${storedToken}`, }, }); const response = (await authenticatedClient.request(TEST_CONNECTOR_MUTATION, { id })) as { testConnector: { success: boolean; message?: string; }; }; alert(response.testConnector.message || "Test terminé"); } catch (err: unknown) { console.error("Erreur lors du test:", err); } }; const openConfigDialog = (factory: FactoryConnector) => { setSelectedFactory(factory); setShowConfig(true); // Initialiser le formulaire avec les valeurs par défaut const initialForm: Record = {}; factory.formItems.forEach(item => { if (item.defaultValue !== undefined) { initialForm[item.key] = item.defaultValue; } }); setConfigForm(initialForm); }; const getConnectorName = (nameObj: string | Record): string => { if (typeof nameObj === "object" && nameObj !== null) { return nameObj.en || nameObj.fr || Object.values(nameObj)[0] || "Connecteur"; } return nameObj || "Connecteur"; }; const getConnectorDescription = (descObj: string | Record): string => { if (typeof descObj === "object" && descObj !== null) { return descObj.en || descObj.fr || Object.values(descObj)[0] || ""; } return descObj || ""; }; return (

Gestion des Connecteurs Logto

{error && (

{error}

)} {loading ? (

Chargement des connecteurs...

) : (
{/* Connecteurs configurés */}

Connecteurs configurés ({connectors.length})

{connectors.length === 0 ? (

Aucun connecteur configuré

) : (
{connectors.map(connector => (
{getConnectorName(connector.metadata.name)} {connector.enabled ? "Activé" : "Désactivé"}

{getConnectorName(connector.metadata.name)}

{connector.metadata.platform || connector.type}

))}
)}
{/* Connecteurs disponibles */}

Connecteurs disponibles

{availableConnectors.map(factory => (
{getConnectorName(factory.name)} {factory.isAdded && ( Ajouté )}

{getConnectorName(factory.name)}

{getConnectorDescription(factory.description)}

{!factory.isAdded && ( )}
))}
)} {/* Dialog de configuration */} {showConfig && selectedFactory && (

Configurer {getConnectorName(selectedFactory.name)}

{ e.preventDefault(); handleCreateConnector(); }} className="space-y-4" > {selectedFactory.formItems.map(item => (
{item.type === "Password" ? ( setConfigForm({ ...configForm, [item.key]: e.target.value })} className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500" /> ) : item.type === "Switch" ? ( setConfigForm({ ...configForm, [item.key]: e.target.checked })} className="mt-1" /> ) : item.type === "MultiSelect" ? ( ) : item.type === "Number" ? ( setConfigForm({ ...configForm, [item.key]: Number(e.target.value) })} placeholder={ typeof item.placeholder === "object" && item.placeholder !== null ? item.placeholder.en || item.placeholder.fr : item.placeholder || "" } className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500" /> ) : ( setConfigForm({ ...configForm, [item.key]: e.target.value })} placeholder={ typeof item.placeholder === "object" && item.placeholder !== null ? item.placeholder.en || item.placeholder.fr : item.placeholder || "" } className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500" /> )} {item.description && (

{typeof item.description === "object" && item.description !== null ? item.description.en || item.description.fr : item.description}

)}
))}
)} {/* Navigation */}
); }