diff --git a/application/src/App.tsx b/application/src/App.tsx index 2068c31..02a1ba1 100644 --- a/application/src/App.tsx +++ b/application/src/App.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { Toaster } from '@/components/ui/sonner'; import { ThemeProvider } from '@/contexts/ThemeContext'; import { LanguageProvider } from '@/contexts/LanguageContext'; import { SidebarProvider } from '@/contexts/SidebarContext'; +import { Toaster } from '@/components/ui/toaster'; import { ErrorBoundary } from '@/components/ErrorBoundary'; // Pages @@ -13,51 +13,103 @@ import Index from '@/pages/Index'; import Login from '@/pages/Login'; import Dashboard from '@/pages/Dashboard'; import ServiceDetail from '@/pages/ServiceDetail'; -import Profile from '@/pages/Profile'; import Settings from '@/pages/Settings'; -import OperationalPage from '@/pages/OperationalPage'; -import ScheduleIncident from '@/pages/ScheduleIncident'; +import Profile from '@/pages/Profile'; import SslDomain from '@/pages/SslDomain'; +import ScheduleIncident from '@/pages/ScheduleIncident'; +import OperationalPage from '@/pages/OperationalPage'; import PublicStatusPage from '@/pages/PublicStatusPage'; +import RegionalMonitoring from '@/pages/RegionalMonitoring'; import NotFound from '@/pages/NotFound'; +import { authService } from '@/services/authService'; + const queryClient = new QueryClient({ defaultOptions: { queries: { - retry: 1, - refetchOnWindowFocus: false, + retry: 2, + staleTime: 5 * 60 * 1000, // 5 minutes }, }, }); +// Protected Route wrapper +const ProtectedRoute = ({ children }: { children: React.ReactNode }) => { + const isAuthenticated = authService.isAuthenticated(); + return isAuthenticated ? <>{children} : ; +}; + function App() { return ( - - - - - + + + + + } /> } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + + {/* Protected Routes */} + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + } /> - - - - - + + + + + ); } diff --git a/application/src/components/dashboard/sidebar/navigationData.ts b/application/src/components/dashboard/sidebar/navigationData.ts index 486e395..a96035f 100644 --- a/application/src/components/dashboard/sidebar/navigationData.ts +++ b/application/src/components/dashboard/sidebar/navigationData.ts @@ -1,5 +1,4 @@ - -import { Globe, Boxes, Radar, Calendar, BarChart2, LineChart, FileText, Settings, User, Bell, Database, Info, BookOpen } from "lucide-react"; +import { Globe, Boxes, Radar, Calendar, BarChart2, LineChart, MapPin, Settings, User, Bell, Database, Info, BookOpen } from "lucide-react"; export const mainMenuItems = [ { @@ -51,12 +50,12 @@ export const mainMenuItems = [ hasNavigation: false }, { - id: 'api-documentation', - path: null, - icon: FileText, - translationKey: 'apiDocumentation', + id: 'regional-monitoring', + path: '/regional-monitoring', + icon: MapPin, + translationKey: 'regionalMonitoring', color: 'text-indigo-400', - hasNavigation: false + hasNavigation: true } ]; diff --git a/application/src/components/regional-monitoring/AddRegionalAgentDialog.tsx b/application/src/components/regional-monitoring/AddRegionalAgentDialog.tsx new file mode 100644 index 0000000..14d8bd4 --- /dev/null +++ b/application/src/components/regional-monitoring/AddRegionalAgentDialog.tsx @@ -0,0 +1,403 @@ + +import React, { useState } from "react"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Copy, Download, Terminal, CheckCircle, Zap, Play } from "lucide-react"; +import { regionalService } from "@/services/regionalService"; +import { InstallCommand } from "@/types/regional.types"; +import { useToast } from "@/hooks/use-toast"; + +interface AddRegionalAgentDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onAgentAdded: () => void; +} + +export const AddRegionalAgentDialog: React.FC = ({ + open, + onOpenChange, + onAgentAdded +}) => { + const [step, setStep] = useState(1); + const [regionName, setRegionName] = useState(""); + const [agentIp, setAgentIp] = useState(""); + const [installCommand, setInstallCommand] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + const { toast } = useToast(); + + const handleCreateAgent = async (e: React.FormEvent) => { + e.preventDefault(); + if (!regionName.trim() || !agentIp.trim()) return; + + setIsSubmitting(true); + try { + const result = await regionalService.createRegionalService({ + region_name: regionName, + agent_ip_address: agentIp, + }); + + setInstallCommand(result.installCommand); + setStep(2); + } catch (error) { + toast({ + title: "Error", + description: "Failed to create regional agent configuration.", + variant: "destructive", + }); + } finally { + setIsSubmitting(false); + } + }; + + const copyToClipboard = async (text: string) => { + try { + await navigator.clipboard.writeText(text); + toast({ + title: "Copied!", + description: "Installation script copied to clipboard.", + }); + } catch (error) { + toast({ + title: "Copy failed", + description: "Failed to copy to clipboard.", + variant: "destructive", + }); + } + }; + + const downloadScript = () => { + if (!installCommand) return; + + const blob = new Blob([installCommand.bash_script], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `install-regional-agent-${installCommand.agent_id}.sh`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + + toast({ + title: "Downloaded!", + description: "Installation script downloaded successfully.", + }); + }; + + const copyOneClickCommand = () => { + if (!installCommand) return; + + const oneClickCommand = `curl -fsSL -H "User-Agent: CheckCle-Installer" \\ + "data:text/plain;base64,$(echo '${installCommand.bash_script}' | base64 -w 0)" \\ + | sudo bash`; + + copyToClipboard(oneClickCommand); + }; + + const handleComplete = () => { + onAgentAdded(); + setStep(1); + setRegionName(""); + setAgentIp(""); + setInstallCommand(null); + onOpenChange(false); + }; + + const resetDialog = () => { + setStep(1); + setRegionName(""); + setAgentIp(""); + setInstallCommand(null); + }; + + return ( + + + + Add Regional Monitoring Agent + + Deploy a regional monitoring agent with automatic one-click installation. + + + + {step === 1 && ( +
+
+
+ + setRegionName(e.target.value)} + required + /> +
+ +
+ + setAgentIp(e.target.value)} + required + /> +
+
+ +
+ + +
+
+ )} + + {step === 2 && installCommand && ( +
+
+ +

Agent Configuration Ready!

+

+ One-click installation script generated with automatic configuration. +

+
+ + + + One-Click Install + Agent Details + Manual Install + + + + + + + + One-Click Automatic Installation + + + Complete installation, configuration, and service startup in one command + + + +
+
+ + What this script does automatically: +
+
    +
  • • Downloads the latest .deb package
  • +
  • • Installs the regional monitoring agent
  • +
  • • Creates configuration file with your settings
  • +
  • • Starts and enables the service
  • +
  • • Runs health checks
  • +
+
+ +
+ +
+