From 2c36650edd8be22d86aa7c67e6441d8834363f0a Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Wed, 3 Sep 2025 14:11:12 +0000 Subject: [PATCH] Add SQL execution tool for administrators to manage database tables Introduce a new admin-only utility tab allowing direct SQL query execution, file uploads (.sql), and logging of operations. Includes server-side endpoint and client-side UI for executing raw SQL commands against the database. Replit-Commit-Author: Agent Replit-Commit-Session-Id: cfbfe47c-0c9a-4ebf-8a41-2937407eccff Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/cfbfe47c-0c9a-4ebf-8a41-2937407eccff/ECRh2Iu --- .replit | 2 +- client/src/pages/SQLExecutor.tsx | 236 +++++++++++++++++++++++++ client/src/pages/Utilities.tsx | 23 ++- fix-webhook-bap-FINAL.sql | 41 ----- scripts/manual-create-webhook-table.sh | 56 ------ server/routes.ts | 67 +++++++ 6 files changed, 324 insertions(+), 101 deletions(-) create mode 100644 client/src/pages/SQLExecutor.tsx delete mode 100644 fix-webhook-bap-FINAL.sql delete mode 100644 scripts/manual-create-webhook-table.sh diff --git a/.replit b/.replit index 853b5d3..e7ed764 100644 --- a/.replit +++ b/.replit @@ -37,4 +37,4 @@ args = "npm run dev" waitForPort = 5000 [agent] -integrations = ["javascript_database==1.0.0", "javascript_object_storage==1.0.0", "javascript_log_in_with_replit==1.0.0"] +integrations = ["javascript_log_in_with_replit==1.0.0", "javascript_database==1.0.0", "javascript_object_storage==1.0.0"] diff --git a/client/src/pages/SQLExecutor.tsx b/client/src/pages/SQLExecutor.tsx new file mode 100644 index 0000000..832a70f --- /dev/null +++ b/client/src/pages/SQLExecutor.tsx @@ -0,0 +1,236 @@ +import { useState, useRef } from "react"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Textarea } from "@/components/ui/textarea"; +import { useToast } from "@/hooks/use-toast"; +import { Upload, Play, Copy, AlertTriangle, Database } from "lucide-react"; +import { useMutation } from "@tanstack/react-query"; +import { apiRequest } from "@/lib/queryClient"; + +export default function SQLExecutor() { + const { toast } = useToast(); + const [sqlContent, setSqlContent] = useState(""); + const [logs, setLogs] = useState([]); + const [isExecuting, setIsExecuting] = useState(false); + const fileInputRef = useRef(null); + + const executeMutation = useMutation({ + mutationFn: async (sql: string) => { + setLogs([]); + setIsExecuting(true); + + const response = await fetch('/api/admin/execute-sql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ sql }), + credentials: 'include' + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || 'Erreur lors de l\'exécution SQL'); + } + + return response.json(); + }, + onSuccess: (result) => { + setLogs(prev => [...prev, '✅ Exécution SQL terminée avec succès']); + if (result.logs) { + setLogs(prev => [...prev, ...result.logs]); + } + if (result.results) { + setLogs(prev => [...prev, `📊 Résultats: ${JSON.stringify(result.results, null, 2)}`]); + } + + toast({ + title: "Succès", + description: "SQL exécuté avec succès", + }); + }, + onError: (error: any) => { + setLogs(prev => [...prev, `❌ Erreur: ${error.message}`]); + + toast({ + title: "Erreur", + description: error.message, + variant: "destructive", + }); + }, + onSettled: () => { + setIsExecuting(false); + } + }); + + const handleFileUpload = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + if (!file.name.endsWith('.sql')) { + toast({ + title: "Erreur", + description: "Veuillez sélectionner un fichier .sql", + variant: "destructive", + }); + return; + } + + const reader = new FileReader(); + reader.onload = (e) => { + const content = e.target?.result as string; + setSqlContent(content); + setLogs([`📁 Fichier ${file.name} chargé (${content.length} caractères)`]); + }; + reader.readAsText(file); + }; + + const handleExecuteSQL = () => { + if (!sqlContent.trim()) { + toast({ + title: "Erreur", + description: "Veuillez saisir du SQL à exécuter", + variant: "destructive", + }); + return; + } + + setLogs([`🔄 Début de l'exécution SQL...`]); + executeMutation.mutate(sqlContent); + }; + + const handleCopyWebhookSQL = () => { + const webhookSQL = `-- Script pour créer la table webhook_bap_config +CREATE TABLE IF NOT EXISTS webhook_bap_config ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL DEFAULT 'Configuration BAP', + webhook_url TEXT NOT NULL, + description TEXT, + is_active BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Insérer la configuration par défaut +INSERT INTO webhook_bap_config (name, webhook_url, description, is_active) +SELECT + 'Configuration BAP', + 'https://workflow.ffnancy.fr/webhook/a3d03176-b72f-412d-8fb9-f920b9fbab4d', + 'Configuration par défaut pour envoi des fichiers BAP vers n8n', + true +WHERE NOT EXISTS (SELECT 1 FROM webhook_bap_config); + +-- Commentaire sur la table +COMMENT ON TABLE webhook_bap_config IS 'Configuration pour webhook BAP n8n'; + +-- Vérification finale +SELECT 'Table webhook_bap_config créée!' AS message; +SELECT COUNT(*) AS nb_configs FROM webhook_bap_config;`; + + setSqlContent(webhookSQL); + navigator.clipboard.writeText(webhookSQL); + + toast({ + title: "SQL copié", + description: "Le script webhook BAP a été copié et collé", + }); + }; + + return ( +
+
+ +

Exécution SQL

+
+ + + + + + Avertissement de Sécurité + + + Cette fonctionnalité permet d'exécuter du SQL directement sur la base de données. + Utilisez-la avec précaution et uniquement pour des opérations de maintenance. + + + + +
+ + + Script SQL + + Collez votre SQL ou uploadez un fichier .sql + + + +
+ + + +
+ + + +