From 0fbc317e926b5d80363979ee51a4e3c930014efd Mon Sep 17 00:00:00 2001 From: Matt Strapp Date: Fri, 11 Feb 2022 00:11:52 -0600 Subject: Do a bunch of random things (still no feature parity) sadge Signed-off-by: Matt Strapp --- src/index.ts | 56 ++++++++++++------------ src/public/css/style.css | 101 ++++---------------------------------------- src/public/js/form.js | 17 ++++++++ src/routes/api.ts | 40 ++++++++++++++++++ src/routes/api/actuate.ts | 0 src/routes/api/login.ts | 0 src/views/pages/about.ejs | 6 +-- src/views/pages/index.ejs | 76 ++++++--------------------------- src/views/pages/login.ejs | 11 +++++ src/views/partials/head.ejs | 10 ++--- src/views/partials/nav.ejs | 14 +++--- 11 files changed, 132 insertions(+), 199 deletions(-) create mode 100644 src/public/js/form.js create mode 100644 src/routes/api.ts delete mode 100644 src/routes/api/actuate.ts delete mode 100644 src/routes/api/login.ts (limited to 'src') 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 diff --git a/src/routes/api/login.ts b/src/routes/api/login.ts deleted file mode 100644 index e69de29..0000000 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 @@ About the Remotely Accessible Inverted Pendulum
Pendulum logo -

Remotely Accessible Inverted Pendulum

+

Remotely Accessible Inverted Pendulum

-

+

Created by
Abrar Nair Islam, Brendan Lake, Cory Ohnsted, Matt Strapp, Kiflu Woldegiorgis (2022)
Sam Hansen, Rezkath Awal, Donovan Peterson, Joseph Jewett (2021)
Alin Butoi, Paul D’Amico, Dat Nguyen, Ross Olson, Rachel Schow (2019)

Advisor: Professor Andrew Lamperski
Sponsored by the University of Minnesota -Twin Cities
-

+

Background

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 @@ <%- include('../partials/head.ejs') %> - + + <%- include('../partials/nav.ejs') %> -

- - -
-
- -

Homepage

-
- - -

Please upload a Python file (.py file extension) onto the Inverted Pendulum

- -
-

-

-
- - For file upload progress bar -
-
-
- - - <% if (errors) { %> - <% for (const message in errors) { %> - - <% } %> - <% } %> - - - <% if (errors) { %> - <% for (const message in errors) { %> - - <% } %> - <% } %> - - -
-
- - - -
-
-
-
-
-
+
+

Please upload a Python file (.py file extension) onto the Inverted Pendulum.

+

+
+ + +

+

+ + +
\ 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 @@ + + + + + + <%- include('../partials/head.ejs') %> + + + + <%- include('../partials/nav.ejs') %> + \ 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 @@ - - - + + + - - \ No newline at end of file + + \ 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 @@ - \ No newline at end of file + \ No newline at end of file -- cgit v1.2.3