App.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
  2. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  3. import { Layout } from './components/Layout';
  4. import { PrintersPage } from './pages/PrintersPage';
  5. import { ArchivesPage } from './pages/ArchivesPage';
  6. import { QueuePage } from './pages/QueuePage';
  7. import { StatsPage } from './pages/StatsPage';
  8. import { SettingsPage } from './pages/SettingsPage';
  9. import { ProfilesPage } from './pages/ProfilesPage';
  10. import { MaintenancePage } from './pages/MaintenancePage';
  11. import { ProjectsPage } from './pages/ProjectsPage';
  12. import { ProjectDetailPage } from './pages/ProjectDetailPage';
  13. import { FileManagerPage } from './pages/FileManagerPage';
  14. import { CameraPage } from './pages/CameraPage';
  15. import { StreamOverlayPage } from './pages/StreamOverlayPage';
  16. import { ExternalLinkPage } from './pages/ExternalLinkPage';
  17. import { GroupEditPage } from './pages/GroupEditPage';
  18. import InventoryPage from './pages/InventoryPage';
  19. import { SystemInfoPage } from './pages/SystemInfoPage';
  20. import { LoginPage } from './pages/LoginPage';
  21. import { SetupPage } from './pages/SetupPage';
  22. import { NotificationsPage } from './pages/NotificationsPage';
  23. import { useWebSocket } from './hooks/useWebSocket';
  24. import { ThemeProvider } from './contexts/ThemeContext';
  25. import { ToastProvider } from './contexts/ToastContext';
  26. import { AuthProvider, useAuth } from './contexts/AuthContext';
  27. import { SpoolBuddyLayout } from './components/spoolbuddy/SpoolBuddyLayout';
  28. import { SpoolBuddyDashboard } from './pages/spoolbuddy/SpoolBuddyDashboard';
  29. import { SpoolBuddyAmsPage } from './pages/spoolbuddy/SpoolBuddyAmsPage';
  30. import { SpoolBuddySettingsPage } from './pages/spoolbuddy/SpoolBuddySettingsPage';
  31. import { SpoolBuddyCalibrationPage } from './pages/spoolbuddy/SpoolBuddyCalibrationPage';
  32. import { SpoolBuddyWriteTagPage } from './pages/spoolbuddy/SpoolBuddyWriteTagPage';
  33. import { SpoolBuddyInventoryPage } from './pages/spoolbuddy/SpoolBuddyInventoryPage';
  34. const queryClient = new QueryClient({
  35. defaultOptions: {
  36. queries: {
  37. staleTime: 1000 * 60,
  38. retry: 1,
  39. },
  40. },
  41. });
  42. function WebSocketProvider({ children }: { children: React.ReactNode }) {
  43. useWebSocket();
  44. return <>{children}</>;
  45. }
  46. function ProtectedRoute({ children }: { children: React.ReactNode }) {
  47. const { authEnabled, loading, user } = useAuth();
  48. if (loading) {
  49. return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
  50. }
  51. if (authEnabled && !user) {
  52. return <Navigate to="/login" replace />;
  53. }
  54. return <>{children}</>;
  55. }
  56. function AdminRoute({ children }: { children: React.ReactNode }) {
  57. const { authEnabled, loading, user, isAdmin } = useAuth();
  58. if (loading) {
  59. return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
  60. }
  61. // If auth is not enabled, allow access (backward compatibility)
  62. if (!authEnabled) {
  63. return <>{children}</>;
  64. }
  65. // If auth is enabled but no user, redirect to login
  66. if (!user) {
  67. return <Navigate to="/login" replace />;
  68. }
  69. // If user is not admin, redirect to home
  70. if (!isAdmin) {
  71. return <Navigate to="/" replace />;
  72. }
  73. return <>{children}</>;
  74. }
  75. function SetupRoute({ children }: { children: React.ReactNode }) {
  76. const { authEnabled, loading } = useAuth();
  77. if (loading) {
  78. return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
  79. }
  80. // If auth is already enabled, redirect to login
  81. // Otherwise, allow access to setup page (even if setup was completed before)
  82. // This allows users to enable auth later if they skipped it during initial setup
  83. if (authEnabled) {
  84. return <Navigate to="/login" replace />;
  85. }
  86. return <>{children}</>;
  87. }
  88. function App() {
  89. return (
  90. <ThemeProvider>
  91. <ToastProvider>
  92. <QueryClientProvider client={queryClient}>
  93. <AuthProvider>
  94. <BrowserRouter>
  95. <Routes>
  96. {/* Setup page - only accessible if auth not enabled */}
  97. <Route path="/setup" element={<SetupRoute><SetupPage /></SetupRoute>} />
  98. {/* Login page */}
  99. <Route path="/login" element={<LoginPage />} />
  100. {/* Camera page - standalone, no layout, no WebSocket (doesn't need real-time updates) */}
  101. <Route path="/camera/:printerId" element={<CameraPage />} />
  102. {/* Stream overlay page - standalone for OBS/streaming embeds, no auth required */}
  103. <Route path="/overlay/:printerId" element={<StreamOverlayPage />} />
  104. {/* SpoolBuddy kiosk UI */}
  105. <Route element={<ProtectedRoute><WebSocketProvider><SpoolBuddyLayout /></WebSocketProvider></ProtectedRoute>}>
  106. <Route path="spoolbuddy" element={<SpoolBuddyDashboard />} />
  107. <Route path="spoolbuddy/ams" element={<SpoolBuddyAmsPage />} />
  108. <Route path="spoolbuddy/write-tag" element={<SpoolBuddyWriteTagPage />} />
  109. <Route path="spoolbuddy/inventory" element={<SpoolBuddyInventoryPage />} />
  110. <Route path="spoolbuddy/settings" element={<SpoolBuddySettingsPage />} />
  111. <Route path="spoolbuddy/calibration" element={<SpoolBuddyCalibrationPage />} />
  112. </Route>
  113. {/* Main app with WebSocket for real-time updates */}
  114. <Route element={<ProtectedRoute><WebSocketProvider><Layout /></WebSocketProvider></ProtectedRoute>}>
  115. <Route index element={<PrintersPage />} />
  116. <Route path="archives" element={<ArchivesPage />} />
  117. <Route path="queue" element={<QueuePage />} />
  118. <Route path="stats" element={<StatsPage />} />
  119. <Route path="profiles" element={<ProfilesPage />} />
  120. <Route path="maintenance" element={<MaintenancePage />} />
  121. <Route path="projects" element={<ProjectsPage />} />
  122. <Route path="projects/:id" element={<ProjectDetailPage />} />
  123. <Route path="inventory" element={<InventoryPage />} />
  124. <Route path="files" element={<FileManagerPage />} />
  125. <Route path="settings" element={<AdminRoute><SettingsPage /></AdminRoute>} />
  126. <Route path="groups/new" element={<AdminRoute><GroupEditPage /></AdminRoute>} />
  127. <Route path="groups/:id/edit" element={<AdminRoute><GroupEditPage /></AdminRoute>} />
  128. <Route path="users" element={<Navigate to="/settings?tab=users" replace />} />
  129. <Route path="groups" element={<Navigate to="/settings?tab=users" replace />} />
  130. <Route path="system" element={<SystemInfoPage />} />
  131. <Route path="notifications" element={<NotificationsPage />} />
  132. <Route path="external/:id" element={<ExternalLinkPage />} />
  133. </Route>
  134. </Routes>
  135. </BrowserRouter>
  136. </AuthProvider>
  137. </QueryClientProvider>
  138. </ToastProvider>
  139. </ThemeProvider>
  140. );
  141. }
  142. export default App;