File size: 3,065 Bytes
df1fcb2
 
61aec16
92dff23
 
ffcd038
d212ba6
f6d96e2
61aec16
2471e68
df1fcb2
92dff23
df1fcb2
 
 
5e07ced
df1fcb2
 
 
 
d97497a
5e07ced
 
d97497a
5e07ced
 
 
d97497a
 
df1fcb2
ffcd038
 
 
 
 
 
df1fcb2
 
5e07ced
 
ffcd038
 
 
 
 
df1fcb2
 
 
 
 
 
 
 
 
 
 
9a238c2
df1fcb2
 
 
5e07ced
 
 
 
 
df1fcb2
 
 
 
 
 
 
d212ba6
df1fcb2
 
5e07ced
 
 
 
92dff23
 
 
2471e68
92dff23
f6d96e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92dff23
2471e68
 
61aec16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { useEffect, useState } from 'react';
import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation } from 'react-router-dom';
import { MainApp } from './pages/MainApp';
import { Login } from './pages/Login';
import { Settings } from './pages/Settings';
import { isAuthenticated, getCurrentUser, setAuthTokenFromHash, ensureDemoToken, isDemoSession } from './services/auth';
import { AuthLoadingSkeleton } from './components/AuthLoadingSkeleton';
import { Toaster } from './components/ui/toaster';
import './App.css';

// Protected route wrapper with auth check
function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const navigate = useNavigate();
  const location = useLocation();
  const [isChecking, setIsChecking] = useState(true);
  const [hasToken, setHasToken] = useState<boolean>(isAuthenticated());

  // T110: Check if user is authenticated on mount
  useEffect(() => {
    const checkAuth = async () => {
      // Check for OAuth callback token in URL hash
      const tokenExtracted = setAuthTokenFromHash();
      if (tokenExtracted) {
        console.log('OAuth token extracted from URL hash');
        setHasToken(true);
        setIsChecking(false);
        return;
      }

      if (!isAuthenticated()) {
        const demoReady = await ensureDemoToken();
        if (!demoReady) {
          setHasToken(false);
          setIsChecking(false);
          return;
        }
      }

      setHasToken(true);

      if (isDemoSession()) {
        setIsChecking(false);
        return;
      }

      const token = localStorage.getItem('auth_token');
      // Skip validation for local dev token
      if (token === 'local-dev-token') {
        setIsChecking(false);
        return;
      }

      try {
        // Verify the token is valid by calling getCurrentUser
        await getCurrentUser();
        setIsChecking(false);
      } catch {
        // Token is invalid (401), redirect to login
        console.warn('Authentication failed, redirecting to login');
        localStorage.removeItem('auth_token');
        setHasToken(false);
        setIsChecking(false);
        if (location.pathname !== '/login') {
          navigate('/login', { replace: true, state: { from: location } });
        }
      }
    };

    checkAuth();
  }, [navigate, location]);

  if (isChecking) {
    return <AuthLoadingSkeleton />;
  }

  if (!hasToken) {
    return <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}

function App() {
  return (
    <>
      <BrowserRouter>
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route
            path="/"
            element={
              <ProtectedRoute>
                <MainApp />
              </ProtectedRoute>
            }
          />
          <Route
            path="/settings"
            element={
              <ProtectedRoute>
                <Settings />
              </ProtectedRoute>
            }
          />
        </Routes>
      </BrowserRouter>
      <Toaster />
    </>
  );
}

export default App;