aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/index.ts56
-rw-r--r--src/public/css/style.css101
-rw-r--r--src/public/js/form.js17
-rw-r--r--src/routes/api.ts40
-rw-r--r--src/routes/api/actuate.ts0
-rw-r--r--src/routes/api/login.ts0
-rw-r--r--src/views/pages/about.ejs6
-rw-r--r--src/views/pages/index.ejs76
-rw-r--r--src/views/pages/login.ejs11
-rw-r--r--src/views/partials/head.ejs10
-rw-r--r--src/views/partials/nav.ejs14
11 files changed, 132 insertions, 199 deletions
diff --git a/src/index.ts b/src/index.ts
index 9e7d082..bd2c7d5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,56 +1,54 @@
import express, { Request, Response } from 'express';
-import rateLimit from 'express-rate-limit';
-import slowDown from 'express-slow-down';
+
import path from 'path';
import { env } from 'process';
import helmet from 'helmet';
import csurf from 'csurf';
import cookieParser from 'cookie-parser';
+import rateLimit from 'express-rate-limit';
+import api from './routes/api';
const app = express();
-// Middleware
-const port: string = env.PORT || '2000';
+/* MIDDLEWARE */
-app.use(cookieParser());
-const csrf = csurf({ cookie: true });
+// Rate limiting
const rateLimiter = rateLimit({
windowMs: 1 * 60 * 1000, // 1 minute
- max: 30, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
+ max: 40, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
});
-const speedLimiter = slowDown({
- windowMs: 15 * 60 * 1000, // 15 minutes
- delayAfter: 100, // allow 100 requests per 15 minutes, then...
- delayMs: 500 // begin adding 500ms of delay per request above 100:
- // request # 101 is delayed by 500ms
- // request # 102 is delayed by 1000ms
- // request # 103 is delayed by 1500ms
- // etc.
-});
-// This will be run behind an nginx proxy
-app.enable('trust proxy');
-// apply to all requests
-app.use(speedLimiter);
-app.use('/api', rateLimiter);
+app.use(rateLimiter);
+
+// CSRF protection
+app.use(cookieParser());
+const csrf = csurf({ cookie: true });
+
+
+// Hide the software being used (helps security)
app.use(helmet());
-// Add ejs as view engine
-app.set('view engine', 'ejs');
-app.set('views', path.join(__dirname, 'views/pages'));
-app.use('/public', express.static(path.join(__dirname, 'public')));
+// The API
+app.use('/api', api);
+
+/* RENDERING */
+
+app.set('view engine', 'ejs'); // Add ejs as view engine
+app.set('views', path.join(__dirname, 'views/pages')); // Set views directory (where the ejs lies)
+app.use('/public', express.static(path.join(__dirname, 'public'))); // Set static directory (where the static CSS/JS/images lie)
+
+/* ROUTING */
app.get('/', csrf, (req: Request, res: Response) => {
- res.render('index', {
- errors: [],
- });
+ res.render('index', { csrfToken: req.csrfToken() });
});
-
app.get('/about', csrf, (req: Request, res: Response) => {
res.render('about');
});
+// Start the server
+const port = env.PORT || 2000;
app.listen(port, () => {
console.log(`Server is listening on port ${port}`);
}); \ No newline at end of file
diff --git a/src/public/css/style.css b/src/public/css/style.css
index cdcad8c..0e7892d 100644
--- a/src/public/css/style.css
+++ b/src/public/css/style.css
@@ -1,12 +1,13 @@
body {
background-color: black;
-}
-
-.header {
color: white;
text-align: center;
vertical-align: middle;
position: relative;
+}
+
+.header {
+
/* top: 30px; */
margin: 5px;
font-family: "Times New Roman", Times, serif;
@@ -24,17 +25,12 @@ p {
}
-/* Customize the positioning of the logo */
-
-
-
-
/*-------------------------Buttons-----------------------------------------------------*/
/* Used for id=button1 */
-#button1 {
+#actuate_but {
background-color: #0a3f73;
border: solid;
border-color: #d5d6d2;
@@ -51,68 +47,13 @@ p {
/* Used for id=button1 - What the button looks like when a user puts one's cursor on it */
-#button1:hover {
+#actuate_but:hover {
background-color: white;
color: maroon;
cursor: pointer;
}
-/* Used for id=button2 */
-
-#button2 {
- /* #ebcba9 is similar to a tan color */
- background-color: #ebcba9;
- border: solid;
- border-color: #d5d6d2;
- /* #693100 is similar to a brown color */
- color: #693100;
- padding: 10px 20px;
- text-align: center;
- text-decoration: none;
- font-family: "Constantia", sans-serif;
- border-radius: 10px;
- display: inline-block;
- font-size: 20px;
-}
-
-
-/* Used for id=button2 - What the button looks like when a user puts one's cursor on it */
-
-#button2:hover {
- background-color: white;
- color: #0a3f73;
- cursor: pointer;
-}
-
-
-/* Used for id=button2 */
-
-#button3 {
- /* #cf7d4e is similar to a light brown color */
- background-color: #cf7d4e;
- border: solid;
- border-color: #d5d6d2;
- /* #094f02 is similar to a dark green color */
- color: #094f02;
- padding: 10px 20px;
- text-align: center;
- text-decoration: none;
- font-family: "Constantia", sans-serif;
- border-radius: 10px;
- display: inline-block;
- font-size: 20px;
-}
-
-
-/* Used for id=button2 - What the button looks like when a user puts one's cursor on it */
-
-#button3:hover {
- background-color: white;
- color: maroon;
- cursor: pointer;
-}
-
a {
color: white;
text-decoration: none;
@@ -134,12 +75,6 @@ a {
}
-/* Used for id=title1 */
-
-#title1 {
- font-size: xx-large;
-}
-
/* Used for id=title2 */
@@ -158,14 +93,6 @@ a {
margin-top: 15px;
}
-#subtitle {
- font-size: large;
-}
-
-#subtitle1 {
- font-size: 23px;
-}
-
#Background {
color: #ffbb00;
}
@@ -175,14 +102,6 @@ a {
color: #4294cf;
}
-#choose_file {
- border: none;
- text-decoration: none;
- display: inline-block;
-}
-
-#submit {}
-
/* Used for id=Start */
@@ -203,11 +122,11 @@ a {
background-color: #7a0019;
}
-li {
+.navbar li {
float: left;
}
-li a {
+.navbar li a {
display: block;
color: white;
text-align: center;
@@ -216,9 +135,7 @@ li a {
text-decoration: none;
}
-
-
-li a:hover {
+.navbar li a:hover {
background-color: #ffcc33;
color: #000000;
/* Remove underlines from links */
diff --git a/src/public/js/form.js b/src/public/js/form.js
new file mode 100644
index 0000000..cfa80fd
--- /dev/null
+++ b/src/public/js/form.js
@@ -0,0 +1,17 @@
+document.getElementById('upload').onsubmit = function () {
+ var data = new FormData(document.getElementById('upload'));
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/api/upload');
+ xhr.send(data);
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4 && xhr.status == 200) {
+ var response = JSON.parse(xhr.responseText);
+ if (response.success) {
+ document.getElementById('success').style.display = 'block';
+ } else {
+ document.getElementById('error').style.display = 'block';
+ }
+ }
+ };
+ return false;
+};
diff --git a/src/routes/api.ts b/src/routes/api.ts
new file mode 100644
index 0000000..4612c16
--- /dev/null
+++ b/src/routes/api.ts
@@ -0,0 +1,40 @@
+import express, { Request, Response } from 'express';
+import csurf from 'csurf';
+import cookieParser from 'cookie-parser';
+import fileUpload, { UploadedFile } from 'express-fileupload';
+import slowDown from 'express-slow-down';
+
+
+// Slow down everything to prevent DoS attacks
+const speedLimiter = slowDown({
+ windowMs: 5 * 60 * 1000, // 15 minutes
+ delayAfter: 50, // allow 100 requests per 5 minutes, then...
+ delayMs: 500 // begin adding 500ms of delay per request above 100:
+ // request # 101 is delayed by 500ms
+ // request # 102 is delayed by 1000ms
+ // request # 103 is delayed by 1500ms
+ // etc.
+});
+
+
+const api = express.Router();
+
+api.use(fileUpload());
+api.use(speedLimiter);
+
+// CSRF protection
+api.use(cookieParser());
+const csrf = csurf({ cookie: true });
+
+api.post('/upload', csrf, (req: Request, res: Response) => {
+ if (!req.files || Object.keys(req.files).length === 0)
+ return res.status(400).json({ err: 'ENOENT' });
+ // Kludge to prevent a compiler error
+ const file: UploadedFile = req.files.file as UploadedFile;
+ console.log(file.mimetype);
+ if (file.mimetype !== 'text/x-python')
+ return res.status(400).json({ err: 'EINVAL' });
+ res.status(200).json({ err: null });
+});
+
+export default api; \ No newline at end of file
diff --git a/src/routes/api/actuate.ts b/src/routes/api/actuate.ts
deleted file mode 100644
index e69de29..0000000
--- a/src/routes/api/actuate.ts
+++ /dev/null
diff --git a/src/routes/api/login.ts b/src/routes/api/login.ts
deleted file mode 100644
index e69de29..0000000
--- a/src/routes/api/login.ts
+++ /dev/null
diff --git a/src/views/pages/about.ejs b/src/views/pages/about.ejs
index 3b376d5..022354c 100644
--- a/src/views/pages/about.ejs
+++ b/src/views/pages/about.ejs
@@ -12,19 +12,19 @@
<title>About the Remotely Accessible Inverted Pendulum</title>
<div class="header">
<img src="public/img/site_logo.png" alt="Pendulum logo" width="160" height="130" />
- <h1 id="title1">Remotely Accessible Inverted Pendulum</h1>
+ <h1>Remotely Accessible Inverted Pendulum</h1>
</div>
<div id="main">
<div id="about">
- <h2 id="subtitle">
+ <h3 id="subtitle">
Created by <br />
Abrar Nair Islam, Brendan Lake, Cory Ohnsted, Matt Strapp, Kiflu Woldegiorgis (2022) <br />
Sam Hansen, Rezkath Awal, Donovan Peterson, Joseph Jewett (2021) <br />
Alin Butoi, Paul D’Amico, Dat Nguyen, Ross Olson, Rachel Schow (2019) <br /> <br />
Advisor: Professor Andrew Lamperski <br />
Sponsored by the University of Minnesota -Twin Cities <br />
- </h2>
+ </h3>
<br />
<h1 id="Background">Background</h1>
<p>
diff --git a/src/views/pages/index.ejs b/src/views/pages/index.ejs
index 6087732..0e6fe15 100644
--- a/src/views/pages/index.ejs
+++ b/src/views/pages/index.ejs
@@ -4,73 +4,23 @@
<head>
<!-- HTML headers information -->
<%- include('../partials/head.ejs') %>
- <script type="text/javascript" src="/static/js/app.js"></script>
+ <script type="module" src="public/js/form.js" defer></script>
</head>
<body>
+ <!-- Get the navbar -->
<%- include('../partials/nav.ejs') %>
- <div class="header">
- <!-- Get the navbar -->
-
- <div class="row">
- <div class="col">
-
- <h1 id="title2">Homepage</h1>
- <hr>
-
-
- <h3 id="subtitle1">Please upload a Python file (.py file extension) onto the Inverted Pendulum</h3>
- <!--**************************Send form data to web server**************************-->
- <form method="POST" action="/index" enctype="multipart/form-data">
- <p><input type="file" name="file" accept=".py" /></p>
- <p><input type="submit" value="Upload File" onclick="update()"></p>
- </form>
-
- For file upload progress bar
- <div id="myProgress">
- <div id="myBar"></div>
- </div>
-
- <!--The following lines are for the warning flash() messages-->
- <% if (errors) { %>
- <% for (const message in errors) { %>
- <div class="alert alert-warning alert-dismissible fade show" role="alert">
- <span>
- <% message %>
- </span>
- <button type="button" class="close" data-dismiss="alert" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <% } %>
- <% } %>
-
- <!--The following lines are for the danger flash() messages-->
- <% if (errors) { %>
- <% for (const message in errors) { %>
- <div class="alert alert-danger alert-dismissible fade show" role="alert">
- <span>
- <% message %>
- </span>
- <button type="button" class="close" data-dismiss="alert" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <% } %>
- <% } %>
-
-
- <div class="mb-3">
- <div class="form-group">
- <label id="Start">Start pendulum: </label>
- <!-- <button class="btn btn-primary" id="onbutton">Actuate!</button> -->
- <button type="button" id=button1 onclick="window.location.href='{{ url_for( 'run_test_file') }}';">Actuate!</button>
- </div>
- </div>
- <hr>
- </div>
- </div>
- </div>
+ <br />
+ <h2>Please upload a Python file (.py file extension) onto the Inverted Pendulum.</h2>
+ <br /> <br />
+ <form id="upload" enctype="multipart/form-data">
+ <input type="hidden" name="_csrf" value="<%= csrfToken %>">
+ <input type="file" name="file" accept=".py" />
+ <br /> <br />
+ <br /> <br />
+ <label id="Start">Start pendulum: </label>
+ <input type="submit" id="actuate_but" value="Actuate!" />
+ </form>
</body>
</html> \ No newline at end of file
diff --git a/src/views/pages/login.ejs b/src/views/pages/login.ejs
index e69de29..7e77ee2 100644
--- a/src/views/pages/login.ejs
+++ b/src/views/pages/login.ejs
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <!-- HTML headers information -->
+ <%- include('../partials/head.ejs') %>
+</head>
+
+<body>
+ <%- include('../partials/nav.ejs') %>
+</body> \ No newline at end of file
diff --git a/src/views/partials/head.ejs b/src/views/partials/head.ejs
index 02786be..320f0e6 100644
--- a/src/views/partials/head.ejs
+++ b/src/views/partials/head.ejs
@@ -1,6 +1,6 @@
-<meta charset="utf-8" />
-<meta http-equiv="X-UA-Compatible" content="IE=edge" />
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
-<link rel="stylesheet" href="public/css/style.css" type="text/css">
-<link rel="icon" href="public/img/site_logo.png" type="image/x-icon"> \ No newline at end of file
+ <link rel="stylesheet" href="public/css/style.css" type="text/css">
+ <link rel="icon" href="public/img/site_logo.png" type="image/x-icon"> \ No newline at end of file
diff --git a/src/views/partials/nav.ejs b/src/views/partials/nav.ejs
index ab2626b..1ee6c00 100644
--- a/src/views/partials/nav.ejs
+++ b/src/views/partials/nav.ejs
@@ -1,7 +1,7 @@
-<nav>
- <ul class="navbar">
- <li><a href="/">Home</a></li>
- <li><a href="/about">About the Inverted Pendulum</a></li>
- <li><a href="https://github.com/RosstheRoss/4951w-pendulum">GitHub Repo</a></li>
- </ul>
-</nav> \ No newline at end of file
+ <nav>
+ <ul class="navbar">
+ <li><a href="/">Home</a></li>
+ <li><a href="/about">About the Inverted Pendulum</a></li>
+ <li><a href="https://github.com/RosstheRoss/4951w-pendulum">GitHub Repo</a></li>
+ </ul>
+ </nav> \ No newline at end of file