diff --git a/.dockerignore b/.dockerignore index 1aaea6f..9b53056 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,7 +6,7 @@ **/.classpath **/.dockerignore -# **/.env +**/.env **/.git **/.gitignore **/.project diff --git a/client/src/App.tsx b/client/src/App.tsx index 9585a60..2484db0 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { ReactNode, useState } from 'react'; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import Navbar from './components/Navbar'; @@ -7,28 +7,64 @@ import Home from './pages/Home'; import EventsDashboard from './pages/EventsDashboard'; import Login from './pages/Login'; import SignUp from './pages/SignUp'; +import { UserDetails } from './types'; const App: React.FC = () => { const [isDarkMode, setIsDarkMode] = useState(false); // Dark mode state - const [user, setUser] = useState | null>(null); + const [user, setUser] = useState(null); const toggleDarkMode = () => { setIsDarkMode((prev) => !prev); document.body.classList.toggle('dark-mode', !isDarkMode); // Toggle class based on state }; + function checkLogin(component: ReactNode): ReactNode { + return user ? component :

You must login to see this page

; + } + + function checkAWSCreds(component: ReactNode): ReactNode { + if ( + user?.aws_access_key?.length && + user?.aws_region?.length > 0 && + user?.aws_secret_access_key?.length > 0 + ) { + return component; + } + return ( +

+ You must enter your AWS credentials in the profile page to see any data + here. +

+ ); + } + return ( - + } /> } /> - {/* {user !== null && <> */} - } /> - } /> + + ))} + /> + + )} + /> } + element={checkAWSCreds( + checkLogin() + )} /> {/* } */} diff --git a/client/src/components/IpAccessCombined.tsx b/client/src/components/IpAccessCombined.tsx index 7cfed35..30bc7eb 100644 --- a/client/src/components/IpAccessCombined.tsx +++ b/client/src/components/IpAccessCombined.tsx @@ -13,11 +13,13 @@ export default function IpAccessCombined({ useEffect(() => { fetch('/events?countOn=source_ip&includeLocation=true') - .then((response) => response.json()) - .then((data: (IPLocation & CountedEvent)[] | { err: string }) => { - if (!Object.prototype.hasOwnProperty.call(Object, 'err')) - setIpLocCounts(() => data as (IPLocation & CountedEvent)[]); + .then((response) => { + if (response.ok) return response.json(); + throw new Error(response.status + ': ' + response.statusText); }) + .then((data: (IPLocation & CountedEvent)[] | { err: string }) => + setIpLocCounts(() => data as (IPLocation & CountedEvent)[]) + ) .catch((error) => console.warn('IpAccessCombined: fetch error: ' + error) ); diff --git a/client/src/components/Navbar.tsx b/client/src/components/Navbar.tsx index b152c51..a29eb59 100644 --- a/client/src/components/Navbar.tsx +++ b/client/src/components/Navbar.tsx @@ -4,7 +4,12 @@ import { NavbarProps } from '../types'; import LOGO from '../assets/RAILGUIDE.png'; //import '../index.scss'; -const Navbar: React.FC = ({ toggleDarkMode, isDarkMode, username, setUser }) => { +const Navbar: React.FC = ({ + toggleDarkMode, + isDarkMode, + username, + setUser, +}) => { const [dropdownOpen, setDropdownOpen] = useState(false); const dropdownRef = useRef(null); const navigate = useNavigate(); @@ -16,12 +21,16 @@ const Navbar: React.FC = ({ toggleDarkMode, isDarkMode, username, s const handleLogout = () => { console.log('User logged out'); setUser(null); + window.localStorage.removeItem('user'); navigate('/login'); }; useEffect(() => { const handleClickOutside = (event: MouseEvent) => { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { setDropdownOpen(false); } }; @@ -44,16 +53,17 @@ const Navbar: React.FC = ({ toggleDarkMode, isDarkMode, username, s - -
- {username && typeof username === 'string' ? username.toUpperCase() : "USER"} -
- + +
+ {username && typeof username === 'string' + ? username.toUpperCase() + : 'USER'} +
{dropdownOpen && (
diff --git a/client/src/components/charts/EventSource.tsx b/client/src/components/charts/EventSource.tsx index eefbac9..31205fe 100644 --- a/client/src/components/charts/EventSource.tsx +++ b/client/src/components/charts/EventSource.tsx @@ -22,10 +22,12 @@ export default function EventSourceChart() { useEffect(() => { setLoading(true); fetch('/events?countOn=source') - .then((response) => response.json()) + .then((response) => { + if (response.ok) return response.json(); + throw new Error(response.status + ': ' + response.statusText); + }) .then((data: CountedEvent[] | { err: string }) => { - if (!Object.prototype.hasOwnProperty.call(Object, 'err')) - setEvents(data as CountedEvent[]); + setEvents(data as CountedEvent[]); setLoading(false); }) .catch((error) => diff --git a/client/src/components/charts/EventType.tsx b/client/src/components/charts/EventType.tsx index 03ed985..8eb97d4 100644 --- a/client/src/components/charts/EventType.tsx +++ b/client/src/components/charts/EventType.tsx @@ -22,15 +22,17 @@ export default function EventTypeChart() { useEffect(() => { setLoading(true); fetch('/events?countOn=type') - .then((response) => response.json()) + .then((response) => { + if (response.ok) return response.json(); + throw new Error(response.status + ': ' + response.statusText); + }) .then((data: CountedEvent[] | { err: string }) => { - if (!Object.prototype.hasOwnProperty.call(Object, 'err')) - setEvents( - (data as CountedEvent[]).map((event) => ({ - ...event, - type: event.type.replace(/([A-Z])/g, ' $1'), - })) - ); + setEvents( + (data as CountedEvent[]).map((event) => ({ + ...event, + type: event.type.replace(/([A-Z])/g, ' $1'), + })) + ); setLoading(false); }) .catch((error) => @@ -64,7 +66,12 @@ export default function EventTypeChart() { angle={-30} textAnchor="end" /> - + {events.map((data, index) => ( { .catch((error) => console.error('Error fetching geoJSON:', error)); fetch('/events?countOn=source_ip&includeLocation=true') - .then((response) => response.json()) - .then((data: (IPLocation & CountedEvent)[] | { err: string }) => { - if (!Object.prototype.hasOwnProperty.call(Object, 'err')) - setIpData(() => data as (IPLocation & CountedEvent)[]); + .then((response) => { + if (response.ok) return response.json(); + throw new Error(response.status + ': ' + response.statusText); }) + .then((data: (IPLocation & CountedEvent)[] | { err: string }) => + setIpData(() => data as (IPLocation & CountedEvent)[]) + ) .catch((error) => console.warn('Could not fetch event ip counts and locations: ', error) ); diff --git a/client/src/components/charts/IpAccessOverTime.tsx b/client/src/components/charts/IpAccessOverTime.tsx index c9027e9..5edd8da 100644 --- a/client/src/components/charts/IpAccessOverTime.tsx +++ b/client/src/components/charts/IpAccessOverTime.tsx @@ -13,10 +13,12 @@ export default function IpAccessOverTimeChart({ useEffect(() => { setLoading(true); // Set loading to true before fetching data fetch('/events?countOn=time&groupTimeBy=minute') - .then((response) => response.json()) + .then((response) => { + if (response.ok) return response.json(); + throw new Error(response.status + ': ' + response.statusText); + }) .then((data: CountedEvent[] | { err: string }) => { - if (!Object.prototype.hasOwnProperty.call(Object, 'err')) - setIpTimes(() => data as CountedEvent[]); + setIpTimes(() => data as CountedEvent[]); setLoading(false); // Set loading to true before fetching data }) .catch((error) => diff --git a/client/src/components/charts/UserActivity.tsx b/client/src/components/charts/UserActivity.tsx index e403782..73edf4b 100644 --- a/client/src/components/charts/UserActivity.tsx +++ b/client/src/components/charts/UserActivity.tsx @@ -1,5 +1,12 @@ import React, { useState, useEffect } from 'react'; -import { CartesianGrid, XAxis, YAxis, AreaChart, Area, Tooltip } from 'recharts'; +import { + CartesianGrid, + XAxis, + YAxis, + AreaChart, + Area, + Tooltip, +} from 'recharts'; import { SimplifiedEvent } from '../../types'; const UserActivityChart: React.FC = () => { @@ -7,16 +14,21 @@ const UserActivityChart: React.FC = () => { useEffect(() => { fetch('/events?countOn=time&groupTimeBy=minute') - .then((response) => response.json()) - .then((data: { time: string; count: number }[]) => { + .then((response) => { + if (response.ok) return response.json(); + throw new Error(response.status + ': ' + response.statusText); + }) + .then((data: { time: string; count: number }[]) => setData( - data.map((event) => ({ + (data as { time: string; count: number }[]).map((event) => ({ localTime: new Date(event.time).toLocaleString(), count: event.count, })) - ); - }) - .catch((error) => console.warn('Could not fetch event time counts: ', error)); + ) + ) + .catch((error) => + console.warn('Could not fetch event time counts: ', error) + ); }, []); // Format for the X-axis to display Mon, Tue, etc. diff --git a/client/src/pages/EventsDashboard.tsx b/client/src/pages/EventsDashboard.tsx index 0553d15..c8686e9 100644 --- a/client/src/pages/EventsDashboard.tsx +++ b/client/src/pages/EventsDashboard.tsx @@ -11,13 +11,21 @@ const EventsDashboard: React.FC = ({ isDarkMode }) => { const [selectedEvent, setSelectedEvent] = useState(null); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); - const [error] = useState(null); + const [error, setError] = useState(null); useEffect(() => { fetch('/events') - .then((response) => response.json()) + .then((response) => { + if (response.ok) return response.json(); + throw new Error(response.status + ': ' + response.statusText); + }) .then((data: TGEvent[]) => setEvents(() => data)) - .catch((error) => console.warn('Could not fetch events: ', error)); + .catch((error) => { + if (error === '403: Forbidden') + setError('Please enter AWS Credentials to view events'); + else console.warn('Could not fetch events: ', error); + }); + setLoading(false); }, []); diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx index 4d58a2d..af61cde 100644 --- a/client/src/pages/Home.tsx +++ b/client/src/pages/Home.tsx @@ -1,4 +1,4 @@ -import React, { lazy, useState } from 'react'; +import React, { useState } from 'react'; import { DragDropContext, Droppable, @@ -6,16 +6,13 @@ import { DropResult, } from '@hello-pangea/dnd'; import { CardState } from '../types'; - -const Card = lazy(() => import('../components/Card')); -const UserActivityChart = lazy( - () => import('../components/charts/UserActivity') -); -const HeatMap = lazy(() => import('../components/charts/HeatMap')); -const IpAccessCombined = lazy(() => import('../components/IpAccessCombined')); -const EventTypeChart = lazy(() => import('../components/charts/EventType')); -const EventSourceChart = lazy(() => import('../components/charts/EventSource')); -const AnomalyChart = lazy(() => import('../components/charts/AnomalyChart')); +import UserActivityChart from '../components/charts/UserActivity'; +import EventTypeChart from '../components/charts/EventType'; +import EventSourceChart from '../components/charts/EventSource'; +import HeatMap from '../components/charts/HeatMap'; +import IpAccessCombined from '../components/IpAccessCombined'; +import AnomalyChart from '../components/charts/AnomalyChart'; +import Card from '../components/Card'; const Home: React.FC<{ isDarkMode: boolean }> = ({ isDarkMode }) => { // State to track the current IP (null means no IP selected) diff --git a/client/src/pages/Login.tsx b/client/src/pages/Login.tsx index 9b98e51..a16a3bf 100644 --- a/client/src/pages/Login.tsx +++ b/client/src/pages/Login.tsx @@ -1,22 +1,34 @@ -import React, { useState } from "react"; -import { useNavigate, Link } from "react-router-dom"; +import React, { useEffect, useState } from 'react'; +import { useNavigate, Link } from 'react-router-dom'; +import { UserDetails } from '../types'; const Login: React.FC<{ - setUser: React.Dispatch | null>>; + setUser: React.Dispatch>; }> = ({ setUser }) => { - const [localUsername, setLocalUsername] = useState(""); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); + const [localUsername, setLocalUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); const [error, setError] = useState(null); const navigate = useNavigate(); + useEffect(() => { + if (window.localStorage.getItem('user')) { + console.log( + 'using sessioned user: ', + window.localStorage.getItem('user') + ); + setUser(JSON.parse(window.localStorage.getItem('user')!) as UserDetails); + navigate('/profile'); + } + }, [navigate, setUser]); + const handleLogin = async (event: React.FormEvent) => { event.preventDefault(); setError(null); // Basic form validation if ((!localUsername && !email) || (localUsername && email)) { - setError("Please provide either a username or an email, but not both"); + setError('Please provide either a username or an email, but not both'); return; } @@ -24,17 +36,17 @@ const Login: React.FC<{ if (email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { - setError("Please enter a valid email address"); + setError('Please enter a valid email address'); return; } } try { //Send resgiter request to the backend - const response = await fetch("/api/login", { - method: "POST", + const response = await fetch('/api/login', { + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: JSON.stringify({ username: localUsername || null, @@ -42,16 +54,18 @@ const Login: React.FC<{ password, }), }); - const user = await response.json() as {username: string}; + const user = (await response.json()) as UserDetails; if (response.ok) { setUser(user); - console.log("Sign-up successful!"); - navigate("/profile"); + window.localStorage.setItem('user', JSON.stringify(user)); + navigate('/profile'); + } else { + setError('Could Not Log In. Please Try again'); } } catch (err) { - setError("Error logging in. Please try again."); - console.error(err, "Error in login at Login.tsx;"); + setError('Error logging in. Please try again.'); + console.error(err, 'Error in login at Login.tsx;'); } }; diff --git a/client/src/pages/Profile.tsx b/client/src/pages/Profile.tsx index 55869a1..21c222a 100644 --- a/client/src/pages/Profile.tsx +++ b/client/src/pages/Profile.tsx @@ -1,34 +1,66 @@ import React from 'react'; -import { ProfileProps } from "../types"; +import { AWSCredentials, ProfileProps, UserDetails } from '../types'; -const Profile: React.FC = ({ isDarkMode, user }) => { +const Profile: React.FC = ({ isDarkMode, user, setUser }) => { + function handleCredentialSubmit() { + const creds: AWSCredentials = { + aws_access_key: + (document.getElementById('accessKey') as HTMLInputElement | null) + ?.value ?? 'Could not find accessKey element', + aws_secret_access_key: + (document.getElementById('secretAccessKey') as HTMLInputElement | null) + ?.value ?? 'Could not find secretAccessKey element', + aws_region: + (document.getElementById('region') as HTMLInputElement | null)?.value ?? + 'Could not find region element', + }; + fetch('/credentials', { + method: 'POST', + body: JSON.stringify({ + ...creds, + username: user?.username ?? 'Not Logged In', + }), + headers: { + 'Content-Type': 'application/json', + }, + }) + .then((response) => { + if (response.ok) window.alert('Credentials Saved'); + else throw Error('Server Error while updating aws credentials'); + return response.json(); + }) + .then((data: UserDetails) => { + setUser(data); + window.localStorage.setItem('user', JSON.stringify(data)); + }) + .catch((error: Error) => { + console.error(error); + }); + } return (
- Profile
-

Username: {user?.username ?? "Not Logged In"}

+

Username: {user?.username ?? 'Not Logged In'}

-

Display Name: {user?.display_name ?? "Not Logged In"}

+

Display Name: {user?.display_name ?? 'Not Logged In'}

-

Work Email: {user?.work_email ?? "Not Logged In"}

+

Work Email: {user?.work_email ?? 'Not Logged In'}

-

Work Phone: {user?.work_phone ?? "Not Logged In"}

+

Work Phone: {user?.work_phone ?? 'Not Logged In'}

- {/*
-

Company: {user.company}

-
*/} = ({ isDarkMode, user }) => {
- - {/* */} - + + AWS Log-in Information
- {/*}
@@ -86,22 +123,42 @@ const Profile: React.FC = ({ isDarkMode, user }) => {
- +
- +
- +
- +
@@ -111,7 +168,12 @@ const Profile: React.FC = ({ isDarkMode, user }) => {
- +
diff --git a/client/src/types.ts b/client/src/types.ts index 43ac74b..0b526da 100644 --- a/client/src/types.ts +++ b/client/src/types.ts @@ -4,13 +4,27 @@ * ================================= */ +export interface UserDetails extends AWSCredentials { + username: string; + display_name: string; + work_email: string; + work_phone: string; +} + +export interface AWSCredentials { + aws_access_key: string; + aws_secret_access_key: string; + aws_region: string; +} + /** * REACT PROPS TYPES */ export interface ProfileProps { isDarkMode: boolean; - user: Record | null + user: UserDetails | null; + setUser: React.Dispatch>; } export interface CardProps { @@ -37,8 +51,8 @@ export interface EventsDashboardProps { export interface NavbarProps { toggleDarkMode: () => void; isDarkMode: boolean; - username: string | null; - setUser: React.Dispatch | null>>; + username: string | null; + setUser: React.Dispatch>; } export interface EventCardProps { @@ -122,7 +136,6 @@ export interface SimplifiedEvent { count: number; } - export interface CountedEvent extends TGEvent { count: number; } diff --git a/compose-dev.yml b/compose-dev.yml index 0e9dede..6c7079a 100644 --- a/compose-dev.yml +++ b/compose-dev.yml @@ -27,7 +27,7 @@ services: dockerfile: Dockerfile target: dev-deps container_name: trailguide-server-dev - env_file: .env + # env_file: .env environment: - POSTGRES_PASSWORD=secret - POSTGRES_USER=tgadmin diff --git a/scripts/db_init.sql b/scripts/db_init.sql index c57fa30..884979d 100644 --- a/scripts/db_init.sql +++ b/scripts/db_init.sql @@ -28,10 +28,10 @@ CREATE TABLE IF NOT EXISTS events ( ); CREATE TABLE IF NOT EXISTS ips ( - ip VARCHAR(15) PRIMARY KEY, - country VARCHAR(20), - region VARCHAR(20), - city VARCHAR(20), + ip VARCHAR(30) PRIMARY KEY, + country VARCHAR(50), + region VARCHAR(50), + city VARCHAR(50), lat NUMERIC(9, 6), long NUMERIC(9, 6) ); @@ -43,5 +43,8 @@ CREATE TABLE IF NOT EXISTS users( display_name VARCHAR(100), work_email VARCHAR(255) UNIQUE NOT NULL, work_phone VARCHAR(25), - created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + aws_access_key VARCHAR, + aws_secret_access_key VARCHAR, + aws_region VARCHAR ); \ No newline at end of file diff --git a/server/controllers/awsController.js b/server/controllers/awsController.js index e41a312..ee5ec2d 100644 --- a/server/controllers/awsController.js +++ b/server/controllers/awsController.js @@ -1,8 +1,56 @@ import * as timeBuckets from '../utils/timeBuckets.js'; -import { query } from '../models/eventsModel.js'; +import { configureCloudtrailClient, query } from '../models/eventsModel.js'; export default { + setCredentials: (req, res, next) => { + try { + const { aws_access_key, aws_secret_access_key, aws_region } = req.body; + if (!aws_access_key || !aws_secret_access_key || !aws_region) { + console.log(req.body); + return next({ + log: `awsController.setCredentials: Malformed Request: aws_access_key= ${aws_access_key} typeof aws_secret_access_key= ${typeof aws_secret_access_key} aws_region= ${aws_region}`, + status: 400, + message: { err: 'Malformed Request' }, + }); + } + process.env.AWS_ACCESS_KEY_ID = aws_access_key; + process.env.AWS_SECRET_ACCESS_KEY = aws_secret_access_key; + process.env.AWS_aws_region = aws_region; + configureCloudtrailClient(); + res.locals.awsCredentials = { + aws_access_key, + aws_secret_access_key, + aws_region, + }; + return next(); + } catch (error) { + return next({ + log: 'awsController.setCredentials: ' + error, + status: 500, + message: { + err: 'A server error occured', + }, + }); + } + }, + getEvents: async (req, res, next) => { + if ( + !process.env.AWS_ACCESS_KEY_ID || + process.env.AWS_ACCESS_KEY_ID === '' || + !process.env.AWS_SECRET_ACCESS_KEY || + process.env.AWS_SECRET_ACCESS_KEY_ID === '' || + !process.env.AWS_aws_region || + process.env.AWS_aws_region === '' + ) { + return next({ + log: 'awsController.getEvents: trying to get events without an accesskey', + status: 403, + message: { + err: 'AWS Credentials not Authorized', + }, + }); + } try { const result = await query( ` diff --git a/server/controllers/userController.js b/server/controllers/userController.js index 9cedd6d..c1e1fc1 100644 --- a/server/controllers/userController.js +++ b/server/controllers/userController.js @@ -83,7 +83,7 @@ export default { res.locals.loggedinuser = user; return next(); } catch (err) { - next({ + return next({ log: 'userController.loginUser: ' + err, status: 500, message: { @@ -92,6 +92,45 @@ export default { }); } }, + + saveUserAwsCredentials: async (req, res, next) => { + if (!req.body.username) + return next({ + log: 'userController.saveUserAWsCredentials: No username provided in request body', + status: 400, + message: { + err: 'Malformed Request: include a username', + }, + }); + try { + const result = await query( + ` + UPDATE users + SET aws_access_key = $1, + aws_secret_access_key = $2, + aws_region = $3 + WHERE username = $4 + RETURNING *; + `, + [ + res.locals.awsCredentials.aws_access_key, + res.locals.awsCredentials.aws_secret_access_key, + res.locals.awsCredentials.aws_region, + req.body.username, + ] + ); + res.locals.updatedUser = result.rows[0]; + return next(); + } catch (error) { + return next({ + log: 'userController.saveUserAwsCredentials: ' + error, + status: 500, + message: { + err: 'Error when saving credentials', + }, + }); + } + }, }; // getAllUsers: async (req, res, next) => { diff --git a/server/models/eventsModel.js b/server/models/eventsModel.js index a6a0018..4a5034d 100644 --- a/server/models/eventsModel.js +++ b/server/models/eventsModel.js @@ -3,7 +3,7 @@ import { LookupEventsCommand, } from '@aws-sdk/client-cloudtrail'; import pg from 'pg'; -import 'dotenv/config'; +// import 'dotenv/config'; // TODO: USE ENVIRONMENT VARIABLES const pool = new pg.Pool({ @@ -46,24 +46,29 @@ export async function connect() { } let cloudtrailClient; -try { - cloudtrailClient = new CloudTrailClient({ - region: process.env.AWS_REGION, - credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, - }, - }); -} catch (error) { - console.log( - `Cannot create cloudtrail client with following credentials: Access Key: ${ - process.env.AWS_ACCESS_KEY_ID - }, Region: ${ - process.env.AWS_REGION - } Secret Access Key type: ${typeof process.env.AWS_SECRET_ACCESS_KEY}` - ); + +export function configureCloudtrailClient() { + try { + cloudtrailClient = new CloudTrailClient({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, + }); + } catch (error) { + console.log( + `Cannot create cloudtrail client with following credentials: Access Key: ${ + process.env.AWS_ACCESS_KEY_ID + }, Region: ${ + process.env.AWS_REGION + } Secret Access Key type: ${typeof process.env.AWS_SECRET_ACCESS_KEY}` + ); + } } +configureCloudtrailClient(); + async function getLastEvent() { try { const result = await query( @@ -86,14 +91,33 @@ async function updateEvents(next, config = {}) { // continue receiving them // otherwise, find the most recent event in the database, // and get any events more recent than that + + if ( + !cloudtrailClient || + !process.env.AWS_ACCESS_KEY_ID || + process.env.AWS_ACCESS_KEY_ID === '' || + !process.env.AWS_SECRET_ACCESS_KEY || + process.env.AWS_SECRET_ACCESS_KEY === '' || + !process.env.AWS_REGION || + process.env.AWS_REGION === '' + ) + return; + if (!next) { const startTime = await getLastEvent(); if (startTime) config.StartTime = startTime; } - + let data; + try { const command = new LookupEventsCommand(config); - const data = await cloudtrailClient.send(command); - + data = await cloudtrailClient.send(command); + } catch (error) { + console.error( + 'eventsModel.updateEvents: LookupEvents error:' + error.message + ); + return; + } + if (!data) return; for (const event of data.Events) { const cloudtrailevent = JSON.parse(event.CloudTrailEvent); // console.log(cloudtrailevent); diff --git a/server/server.js b/server/server.js index 195f47b..ed6ee18 100644 --- a/server/server.js +++ b/server/server.js @@ -16,7 +16,6 @@ app.post('/api/signup', userController.createUser, (req, res) => { res.status(201).json(res.locals.createdUser); }); - //login router app.post('/api/login', userController.loginUser, (req, res) => { res.status(200).json(res.locals.loggedinuser); @@ -37,6 +36,15 @@ app.get( } ); +app.post( + '/credentials', + awsController.setCredentials, + userController.saveUserAwsCredentials, + (_req, res) => { + return res.status(201).json(res.locals.updatedUser); + } +); + app.use((error, _req, res, _next) => { const DEFAULT_ERROR = { log: 'An Unkown middleware error occurred',