Skip to content

Commit 093c07b

Browse files
authored
Merge pull request #79 from oslabs-beta/aidanprior/credentialsToBackend
feat: Add credential submission and tracking
2 parents 65b2f97 + 6129d15 commit 093c07b

File tree

20 files changed

+423
-134
lines changed

20 files changed

+423
-134
lines changed

.dockerignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
**/.classpath
88
**/.dockerignore
9-
# **/.env
9+
**/.env
1010
**/.git
1111
**/.gitignore
1212
**/.project

client/src/App.tsx

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { ReactNode, useState } from 'react';
22
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
33

44
import Navbar from './components/Navbar';
@@ -7,28 +7,64 @@ import Home from './pages/Home';
77
import EventsDashboard from './pages/EventsDashboard';
88
import Login from './pages/Login';
99
import SignUp from './pages/SignUp';
10+
import { UserDetails } from './types';
1011

1112
const App: React.FC = () => {
1213
const [isDarkMode, setIsDarkMode] = useState(false); // Dark mode state
13-
const [user, setUser] = useState<Record<string, string> | null>(null);
14+
const [user, setUser] = useState<UserDetails | null>(null);
1415

1516
const toggleDarkMode = () => {
1617
setIsDarkMode((prev) => !prev);
1718
document.body.classList.toggle('dark-mode', !isDarkMode); // Toggle class based on state
1819
};
1920

21+
function checkLogin(component: ReactNode): ReactNode {
22+
return user ? component : <p>You must login to see this page</p>;
23+
}
24+
25+
function checkAWSCreds(component: ReactNode): ReactNode {
26+
if (
27+
user?.aws_access_key?.length &&
28+
user?.aws_region?.length > 0 &&
29+
user?.aws_secret_access_key?.length > 0
30+
) {
31+
return component;
32+
}
33+
return (
34+
<p>
35+
You must enter your AWS credentials in the profile page to see any data
36+
here.
37+
</p>
38+
);
39+
}
40+
2041
return (
2142
<Router>
22-
<Navbar toggleDarkMode={toggleDarkMode} isDarkMode={isDarkMode} username={user?.username ?? null} setUser={setUser} />
43+
<Navbar
44+
toggleDarkMode={toggleDarkMode}
45+
isDarkMode={isDarkMode}
46+
username={user?.username ?? null}
47+
setUser={setUser}
48+
/>
2349
<Routes>
2450
<Route path="/login" element={<Login setUser={setUser} />} />
2551
<Route path="/signup" element={<SignUp />} />
26-
{/* {user !== null && <> */}
27-
<Route path="/" element={<Home isDarkMode={isDarkMode} />} />
28-
<Route path="/profile" element={<Profile isDarkMode={isDarkMode} user={user} />} />
52+
53+
<Route
54+
path="/"
55+
element={checkAWSCreds(checkLogin(<Home isDarkMode={isDarkMode} />))}
56+
/>
57+
<Route
58+
path="/profile"
59+
element={checkLogin(
60+
<Profile isDarkMode={isDarkMode} user={user} setUser={setUser} />
61+
)}
62+
/>
2963
<Route
3064
path="/events-dashboard"
31-
element={<EventsDashboard isDarkMode={isDarkMode} />}
65+
element={checkAWSCreds(
66+
checkLogin(<EventsDashboard isDarkMode={isDarkMode} />)
67+
)}
3268
/>
3369
{/* </>} */}
3470
</Routes>

client/src/components/IpAccessCombined.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ export default function IpAccessCombined({
1313

1414
useEffect(() => {
1515
fetch('/events?countOn=source_ip&includeLocation=true')
16-
.then((response) => response.json())
17-
.then((data: (IPLocation & CountedEvent)[] | { err: string }) => {
18-
if (!Object.prototype.hasOwnProperty.call(Object, 'err'))
19-
setIpLocCounts(() => data as (IPLocation & CountedEvent)[]);
16+
.then((response) => {
17+
if (response.ok) return response.json();
18+
throw new Error(response.status + ': ' + response.statusText);
2019
})
20+
.then((data: (IPLocation & CountedEvent)[] | { err: string }) =>
21+
setIpLocCounts(() => data as (IPLocation & CountedEvent)[])
22+
)
2123
.catch((error) =>
2224
console.warn('IpAccessCombined: fetch error: ' + error)
2325
);

client/src/components/Navbar.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { NavbarProps } from '../types';
44
import LOGO from '../assets/RAILGUIDE.png';
55
//import '../index.scss';
66

7-
const Navbar: React.FC<NavbarProps> = ({ toggleDarkMode, isDarkMode, username, setUser }) => {
7+
const Navbar: React.FC<NavbarProps> = ({
8+
toggleDarkMode,
9+
isDarkMode,
10+
username,
11+
setUser,
12+
}) => {
813
const [dropdownOpen, setDropdownOpen] = useState(false);
914
const dropdownRef = useRef<HTMLDivElement>(null);
1015
const navigate = useNavigate();
@@ -16,12 +21,16 @@ const Navbar: React.FC<NavbarProps> = ({ toggleDarkMode, isDarkMode, username, s
1621
const handleLogout = () => {
1722
console.log('User logged out');
1823
setUser(null);
24+
window.localStorage.removeItem('user');
1925
navigate('/login');
2026
};
2127

2228
useEffect(() => {
2329
const handleClickOutside = (event: MouseEvent) => {
24-
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
30+
if (
31+
dropdownRef.current &&
32+
!dropdownRef.current.contains(event.target as Node)
33+
) {
2534
setDropdownOpen(false);
2635
}
2736
};
@@ -44,16 +53,17 @@ const Navbar: React.FC<NavbarProps> = ({ toggleDarkMode, isDarkMode, username, s
4453
<button onClick={toggleDarkMode} className="nav-button">
4554
{isDarkMode ? 'LIGHT MODE' : 'DARK MODE'}
4655
</button>
47-
48-
<div
49-
className="nav-button"
50-
onClick={toggleDropdown}
51-
aria-haspopup="true"
52-
aria-expanded={dropdownOpen}
53-
>
54-
{username && typeof username === 'string' ? username.toUpperCase() : "USER"}
55-
</div>
56-
56+
57+
<div
58+
className="nav-button"
59+
onClick={toggleDropdown}
60+
aria-haspopup="true"
61+
aria-expanded={dropdownOpen}
62+
>
63+
{username && typeof username === 'string'
64+
? username.toUpperCase()
65+
: 'USER'}
66+
</div>
5767
</div>
5868
{dropdownOpen && (
5969
<div className="dropdown" ref={dropdownRef}>

client/src/components/charts/EventSource.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ export default function EventSourceChart() {
2222
useEffect(() => {
2323
setLoading(true);
2424
fetch('/events?countOn=source')
25-
.then((response) => response.json())
25+
.then((response) => {
26+
if (response.ok) return response.json();
27+
throw new Error(response.status + ': ' + response.statusText);
28+
})
2629
.then((data: CountedEvent[] | { err: string }) => {
27-
if (!Object.prototype.hasOwnProperty.call(Object, 'err'))
28-
setEvents(data as CountedEvent[]);
30+
setEvents(data as CountedEvent[]);
2931
setLoading(false);
3032
})
3133
.catch((error) =>

client/src/components/charts/EventType.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@ export default function EventTypeChart() {
2222
useEffect(() => {
2323
setLoading(true);
2424
fetch('/events?countOn=type')
25-
.then((response) => response.json())
25+
.then((response) => {
26+
if (response.ok) return response.json();
27+
throw new Error(response.status + ': ' + response.statusText);
28+
})
2629
.then((data: CountedEvent[] | { err: string }) => {
27-
if (!Object.prototype.hasOwnProperty.call(Object, 'err'))
28-
setEvents(
29-
(data as CountedEvent[]).map((event) => ({
30-
...event,
31-
type: event.type.replace(/([A-Z])/g, ' $1'),
32-
}))
33-
);
30+
setEvents(
31+
(data as CountedEvent[]).map((event) => ({
32+
...event,
33+
type: event.type.replace(/([A-Z])/g, ' $1'),
34+
}))
35+
);
3436
setLoading(false);
3537
})
3638
.catch((error) =>
@@ -64,7 +66,12 @@ export default function EventTypeChart() {
6466
angle={-30}
6567
textAnchor="end"
6668
/>
67-
<Bar dataKey="count" maxBarSize={35} minPointSize={5} style={{ cursor: 'pointer' }}>
69+
<Bar
70+
dataKey="count"
71+
maxBarSize={35}
72+
minPointSize={5}
73+
style={{ cursor: 'pointer' }}
74+
>
6875
{events.map((data, index) => (
6976
<Cell
7077
key={`cell-${index}`}

client/src/components/charts/HeatMap.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ const HeatMap: React.FC = () => {
2525
.catch((error) => console.error('Error fetching geoJSON:', error));
2626

2727
fetch('/events?countOn=source_ip&includeLocation=true')
28-
.then((response) => response.json())
29-
.then((data: (IPLocation & CountedEvent)[] | { err: string }) => {
30-
if (!Object.prototype.hasOwnProperty.call(Object, 'err'))
31-
setIpData(() => data as (IPLocation & CountedEvent)[]);
28+
.then((response) => {
29+
if (response.ok) return response.json();
30+
throw new Error(response.status + ': ' + response.statusText);
3231
})
32+
.then((data: (IPLocation & CountedEvent)[] | { err: string }) =>
33+
setIpData(() => data as (IPLocation & CountedEvent)[])
34+
)
3335
.catch((error) =>
3436
console.warn('Could not fetch event ip counts and locations: ', error)
3537
);

client/src/components/charts/IpAccessOverTime.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ export default function IpAccessOverTimeChart({
1313
useEffect(() => {
1414
setLoading(true); // Set loading to true before fetching data
1515
fetch('/events?countOn=time&groupTimeBy=minute')
16-
.then((response) => response.json())
16+
.then((response) => {
17+
if (response.ok) return response.json();
18+
throw new Error(response.status + ': ' + response.statusText);
19+
})
1720
.then((data: CountedEvent[] | { err: string }) => {
18-
if (!Object.prototype.hasOwnProperty.call(Object, 'err'))
19-
setIpTimes(() => data as CountedEvent[]);
21+
setIpTimes(() => data as CountedEvent[]);
2022
setLoading(false); // Set loading to true before fetching data
2123
})
2224
.catch((error) =>

client/src/components/charts/UserActivity.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
11
import React, { useState, useEffect } from 'react';
2-
import { CartesianGrid, XAxis, YAxis, AreaChart, Area, Tooltip } from 'recharts';
2+
import {
3+
CartesianGrid,
4+
XAxis,
5+
YAxis,
6+
AreaChart,
7+
Area,
8+
Tooltip,
9+
} from 'recharts';
310
import { SimplifiedEvent } from '../../types';
411

512
const UserActivityChart: React.FC = () => {
613
const [data, setData] = useState<SimplifiedEvent[]>([]);
714

815
useEffect(() => {
916
fetch('/events?countOn=time&groupTimeBy=minute')
10-
.then((response) => response.json())
11-
.then((data: { time: string; count: number }[]) => {
17+
.then((response) => {
18+
if (response.ok) return response.json();
19+
throw new Error(response.status + ': ' + response.statusText);
20+
})
21+
.then((data: { time: string; count: number }[]) =>
1222
setData(
13-
data.map((event) => ({
23+
(data as { time: string; count: number }[]).map((event) => ({
1424
localTime: new Date(event.time).toLocaleString(),
1525
count: event.count,
1626
}))
17-
);
18-
})
19-
.catch((error) => console.warn('Could not fetch event time counts: ', error));
27+
)
28+
)
29+
.catch((error) =>
30+
console.warn('Could not fetch event time counts: ', error)
31+
);
2032
}, []);
2133

2234
// Format for the X-axis to display Mon, Tue, etc.

client/src/pages/EventsDashboard.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,21 @@ const EventsDashboard: React.FC<EventsDashboardProps> = ({ isDarkMode }) => {
1111
const [selectedEvent, setSelectedEvent] = useState<TGEvent | null>(null);
1212
const [events, setEvents] = useState<TGEvent[]>([]);
1313
const [loading, setLoading] = useState(true);
14-
const [error] = useState<string | null>(null);
14+
const [error, setError] = useState<string | null>(null);
1515

1616
useEffect(() => {
1717
fetch('/events')
18-
.then((response) => response.json())
18+
.then((response) => {
19+
if (response.ok) return response.json();
20+
throw new Error(response.status + ': ' + response.statusText);
21+
})
1922
.then((data: TGEvent[]) => setEvents(() => data))
20-
.catch((error) => console.warn('Could not fetch events: ', error));
23+
.catch((error) => {
24+
if (error === '403: Forbidden')
25+
setError('Please enter AWS Credentials to view events');
26+
else console.warn('Could not fetch events: ', error);
27+
});
28+
2129
setLoading(false);
2230
}, []);
2331

0 commit comments

Comments
 (0)