diff --git a/flask.conf b/flask.conf index 49348f2..c295b1d 100644 --- a/flask.conf +++ b/flask.conf @@ -7,15 +7,25 @@ server { listen 8080; server_name asimonson.com; - add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' *.chesscomfiles.com *.chess.com *.googletagmanager.com cdn.jsdelivr.net www.google-analytics.com ajax.googleapis.com;"; + add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' *.cloudflare.com *.chesscomfiles.com *.chess.com *.googletagmanager.com cdn.jsdelivr.net www.google-analytics.com ajax.googleapis.com;"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options 'nosniff'; add_header X-Frame-Options 'SAMEORIGIN'; location / { + include proxy_params; proxy_pass http://localhost:5000/; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } -} + location /socket.io { + include proxy_params; + proxy_http_version 1.1; + proxy_buffering off; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_pass http://127.0.0.1:5000/socket.io; + } + +} \ No newline at end of file diff --git a/gunicorn.conf b/gunicorn.conf index 6412df9..334b24e 100644 --- a/gunicorn.conf +++ b/gunicorn.conf @@ -1,3 +1,3 @@ [program:gunicorn] -command=/usr/bin/gunicorn app:app -b localhost:5000 +command=/usr/bin/gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker app:app -b localhost:5000 directory=/deploy/app diff --git a/src/app.py b/src/app.py index 903cfbc..43c4f1e 100644 --- a/src/app.py +++ b/src/app.py @@ -1,44 +1,62 @@ import flask from flask_minify import Minify +from flask_socketio import SocketIO import json -proj = json.load(open('./static/json/projects.json', 'r')) -timeline = json.load(open('./static/json/timeline.json', 'r')) +proj = json.load(open("./static/json/projects.json", "r")) +timeline = json.load(open("./static/json/timeline.json", "r")) +pages = { + "home": { + "template": "home.html", + "title": "Andrew Simonson - Portfolio Home", + "description": "Andrew Simonson's Digital Portfolio home", + "canonical": "", + }, + "projects": { + "template": "projects.html", + "projects": proj, + "title": "Andrew Simonson - Projects", + "description": "Recent projects by Andrew Simonson on his lovely portfolio website :)", + "canonical": "projects", + }, + "about": { + "template": "about.html", + "timeline": timeline, + "title": "Andrew Simonson - About Me", + "description": "About Andrew Simonson", + "canonical": "about", + }, +} app = flask.Flask(__name__) Minify(app=app, html=True, js=True, cssless=True) +socketio = SocketIO(app) + + +@socketio.on("goto") +def goto(location): + sid = flask.request.sid + pagevars = pages[location] + output = [location, flask.render_template(pagevars["template"], var=pagevars), pagevars['title']] + socketio.emit("goto", output, to=sid) @app.route("/") def home(): - return flask.render_template( - "home.html", - title="Andrew Simonson - Portfolio Home", - description="Andrew Simonson's Digital portfolio home", - canonical="", - ) - - -@app.route("/about") -def about(): - return flask.render_template( - "about.html", - timeline=timeline, - title="Andrew Simonson - About Me", - description="About Andrew Simonson", - canonical="about", - ) + pagevars = pages["home"] + return flask.render_template("header.html", var=pagevars) @app.route("/projects") def projects(): - return flask.render_template( - "projects.html", - projects=proj, - title="Andrew Simonson - Projects", - description="Recent projects by Andrew Simonson on his lovely portfolio website :)", - canonical="projects", - ) + pagevars = pages["projects"] + return flask.render_template("header.html", var=pagevars) + + +@app.route("/about") +def about(): + pagevars = pages["about"] + return flask.render_template("header.html", var=pagevars) @app.route("/resume") @@ -54,9 +72,16 @@ def page404(e): try: message = e.length finally: + pagevars = { + "template": "error.html", + "title": f"{eCode} - Simonson", + "description": "Error on Andrew Simonson's Digital Portfolio", + "canonical": "404", + } return ( flask.render_template( - "error.html", + "header.html", + var=pagevars, error=eCode, message=message, title=f"{eCode} - Simonson Portfolio", @@ -75,4 +100,4 @@ if __name__ == "__main__": import sass sass.compile(dirname=("static/scss", "static/css"), output_style="compressed") - app.run(debug=True) + socketio.run(app) diff --git a/src/requirements.txt b/src/requirements.txt index 1246f6b..5e1ed2b 100644 Binary files a/src/requirements.txt and b/src/requirements.txt differ diff --git a/src/static/css/App.css b/src/static/css/App.css index 77710ff..a300a6a 100644 --- a/src/static/css/App.css +++ b/src/static/css/App.css @@ -1,6 +1,6 @@ @font-face { font-family: "neon-future"; - src: url("../fonts/Neon Future.ttf") + src: url("../fonts/NeonFuture.ttf") } @font-face { @@ -114,6 +114,7 @@ a { .name { display: inline; + color: #a0a0a0a0; background: url("../photos/sun.png") 0/5rem no-repeat; background-size: contain; padding-left: 5.5rem; diff --git a/src/static/css/head.css b/src/static/css/head.css index 17ee26a..a77ad06 100644 --- a/src/static/css/head.css +++ b/src/static/css/head.css @@ -1 +1 @@ -.line:not(:first-child){position:absolute;top:0;left:0}.line:nth-child(1){animation:clip 6000ms -600ms linear infinite,glitch1 2500ms -433ms linear infinite}@keyframes glitch1{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-1px);color:#4E9A26}98%{transform:translateX(4px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(2){animation:clip 6000ms -1200ms linear infinite,glitch2 2500ms -234ms linear infinite}@keyframes glitch2{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-2px);color:#4E9A26}98%{transform:translateX(-1px);color:#AC1212}99%{transform:translateX(5px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(3){animation:clip 6000ms -1800ms linear infinite,glitch3 2500ms -189ms linear infinite}@keyframes glitch3{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-4px);color:#4E9A26}98%{transform:translateX(2px);color:#AC1212}99%{transform:translateX(-2px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(4){animation:clip 6000ms -2400ms linear infinite,glitch4 2500ms -105ms linear infinite}@keyframes glitch4{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-3px);color:#4E9A26}98%{transform:translateX(4px);color:#AC1212}99%{transform:translateX(1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(5){animation:clip 6000ms -3000ms linear infinite,glitch5 2500ms -89ms linear infinite}@keyframes glitch5{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-2px);color:#4E9A26}98%{transform:translateX(-2px);color:#AC1212}99%{transform:translateX(3px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(6){animation:clip 6000ms -3600ms linear infinite,glitch6 2500ms -936ms linear infinite}@keyframes glitch6{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(-4px);color:#AC1212}99%{transform:translateX(0px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(7){animation:clip 6000ms -4200ms linear infinite,glitch7 2500ms -594ms linear infinite}@keyframes glitch7{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(5px);color:#AC1212}99%{transform:translateX(-3px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(8){animation:clip 6000ms -4800ms linear infinite,glitch8 2500ms -464ms linear infinite}@keyframes glitch8{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-2px);color:#4E9A26}98%{transform:translateX(5px);color:#AC1212}99%{transform:translateX(1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(9){animation:clip 6000ms -5400ms linear infinite,glitch9 2500ms -520ms linear infinite}@keyframes glitch9{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(-2px);color:#AC1212}99%{transform:translateX(3px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(10){animation:clip 6000ms -6000ms linear infinite,glitch10 2500ms -463ms linear infinite}@keyframes glitch10{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(-3px);color:#fff}100%{transform:translateX(0)}}@keyframes clip{0%{clip-path:polygon(0 100%, 100% 100%, 100% 120%, 0 120%)}100%{clip-path:polygon(0 -20%, 100% -20%, 100% 0%, 0 0)}} +.line:not(:first-child){position:absolute;top:0;left:0}.line:nth-child(1){animation:clip 6000ms -600ms linear infinite,glitch1 2500ms -540ms linear infinite}@keyframes glitch1{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-3px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(2){animation:clip 6000ms -1200ms linear infinite,glitch2 2500ms -210ms linear infinite}@keyframes glitch2{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(-2px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(3){animation:clip 6000ms -1800ms linear infinite,glitch3 2500ms -866ms linear infinite}@keyframes glitch3{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(2px);color:#4E9A26}98%{transform:translateX(0px);color:#AC1212}99%{transform:translateX(4px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(4){animation:clip 6000ms -2400ms linear infinite,glitch4 2500ms -60ms linear infinite}@keyframes glitch4{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-3px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(5){animation:clip 6000ms -3000ms linear infinite,glitch5 2500ms -221ms linear infinite}@keyframes glitch5{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(0px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-4px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(6){animation:clip 6000ms -3600ms linear infinite,glitch6 2500ms -716ms linear infinite}@keyframes glitch6{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(5px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(7){animation:clip 6000ms -4200ms linear infinite,glitch7 2500ms -157ms linear infinite}@keyframes glitch7{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-1px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-2px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(8){animation:clip 6000ms -4800ms linear infinite,glitch8 2500ms -955ms linear infinite}@keyframes glitch8{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(-4px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(9){animation:clip 6000ms -5400ms linear infinite,glitch9 2500ms -54ms linear infinite}@keyframes glitch9{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(0px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(10){animation:clip 6000ms -6000ms linear infinite,glitch10 2500ms -857ms linear infinite}@keyframes glitch10{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(2px);color:#fff}100%{transform:translateX(0)}}@keyframes clip{0%{clip-path:polygon(0 100%, 100% 100%, 100% 120%, 0 120%)}100%{clip-path:polygon(0 -20%, 100% -20%, 100% 0%, 0 0)}} diff --git a/src/static/fonts/Neon Future.ttf b/src/static/fonts/NeonFuture.ttf similarity index 100% rename from src/static/fonts/Neon Future.ttf rename to src/static/fonts/NeonFuture.ttf diff --git a/src/static/js/responsive.js b/src/static/js/responsive.js index 2bbd958..934d7b5 100644 --- a/src/static/js/responsive.js +++ b/src/static/js/responsive.js @@ -87,3 +87,26 @@ function toggleMenu() { } } } + +let socket = io(); + +function emit(event) { + socket.emit(event); +} + +function emitData(event, data) { + socket.emit(event, data) +} + +socket.on('goto', (page) => { + pagename = page[0]; + content = page[1]; + let root = document.getElementById('root'); + root.innerHTML = content; + root.querySelectorAll("script").forEach(x => { + eval(x.innerHTML); + }); + document.querySelector('title').textContent = page[2]; + if (pagename == 'home') pagename = '/'; + history.pushState(null, null, pagename); +}); \ No newline at end of file diff --git a/src/static/json/projects.json b/src/static/json/projects.json index 46eb7c9..2ac76b8 100644 --- a/src/static/json/projects.json +++ b/src/static/json/projects.json @@ -37,7 +37,7 @@ "content": "My Resume, made in LaTeX with a custom design derived by the AltaCV template on OverLeaf", "links": [ ["github", "https://github.com/asimonson1125/Resume", "git repo"], - ["globe", "https://asimonson.com/Resume.pdf/", "Resume"] + ["globe", "https://asimonson.com/Resume.pdf", "Resume"] ] }, "Digital Portfolio": { diff --git a/src/templates/about.html b/src/templates/about.html index 8b5b006..933371a 100644 --- a/src/templates/about.html +++ b/src/templates/about.html @@ -1,4 +1,4 @@ -{% extends "header.html" %} {% block content %} +{% block content %}