+1
-1
README.md
+1
-1
README.md
···
30
30
### From Docker
31
31
```bash
32
32
docker build --build-arg API_URL=http://localhost:8080 -t simplelink .
33
-
docker run simplelink -p 8080:8080 \
33
+
docker run -p 8080:8080 \
34
34
-e JWT_SECRET=change-me-in-production \
35
35
-e DATABASE_URL=postgres://user:password@host:port/database \
36
36
simplelink
+14
-11
frontend/index.html
+14
-11
frontend/index.html
···
1
1
<!doctype html>
2
2
<html lang="en">
3
-
<head>
4
-
<meta charset="UTF-8" />
5
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
-
<title>Vite + React + TS</title>
8
-
</head>
9
-
<body>
10
-
<div id="root"></div>
11
-
<script type="module" src="/src/main.tsx"></script>
12
-
</body>
13
-
</html>
3
+
4
+
<head>
5
+
<meta charset="UTF-8" />
6
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
7
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+
<title>SimpleLink</title>
9
+
</head>
10
+
11
+
<body>
12
+
<div id="root"></div>
13
+
<script type="module" src="/src/main.tsx"></script>
14
+
</body>
15
+
16
+
</html>
+14
frontend/src/api/client.ts
+14
frontend/src/api/client.ts
···
15
15
return config;
16
16
});
17
17
18
+
api.interceptors.response.use(
19
+
(response) => response,
20
+
(error) => {
21
+
if (error.response?.status === 401) {
22
+
localStorage.removeItem('token');
23
+
localStorage.removeItem('user');
24
+
25
+
window.dispatchEvent(new Event('unauthorized'));
26
+
}
27
+
return Promise.reject(error);
28
+
}
29
+
);
30
+
31
+
18
32
// Auth endpoints
19
33
export const login = async (email: string, password: string) => {
20
34
const response = await api.post<AuthResponse>('/auth/login', {
+10
frontend/src/context/AuthContext.tsx
+10
frontend/src/context/AuthContext.tsx
···
23
23
setUser(userData);
24
24
}
25
25
setIsLoading(false);
26
+
27
+
const handleUnauthorized = () => {
28
+
setUser(null);
29
+
};
30
+
31
+
window.addEventListener('unauthorized', handleUnauthorized);
32
+
33
+
return () => {
34
+
window.removeEventListener('unauthorized', handleUnauthorized);
35
+
};
26
36
}, []);
27
37
28
38
const login = async (email: string, password: string) => {