Basic contact page
This commit is contained in:
@@ -2,16 +2,61 @@
|
|||||||
|
|
||||||
from index import app
|
from index import app
|
||||||
from os import environ
|
from os import environ
|
||||||
from flask import request, redirect, render_template
|
from flask import request, render_template
|
||||||
|
from requests import post, get
|
||||||
|
from uuid import uuid4
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
def validate_turnstile(response: str, ip: str) -> bool:
|
||||||
|
turnstile_secret = environ['TURNSTILE_SECRET']
|
||||||
|
cf_response = post(
|
||||||
|
url='https://challenges.cloudflare.com/turnstile/v0/siteverify',
|
||||||
|
data={
|
||||||
|
'secret': turnstile_secret,
|
||||||
|
'response': response,
|
||||||
|
'remoteip': ip,
|
||||||
|
'idempotency_key': uuid4()
|
||||||
|
}
|
||||||
|
).json()
|
||||||
|
|
||||||
|
return cf_response.get('success', False)
|
||||||
|
|
||||||
|
def send_to_discord(form: dict) -> bool:
|
||||||
|
try:
|
||||||
|
discord_hook = environ['DISCORD_WEBHOOK']
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
discord_msg = dedent(
|
||||||
|
f'''
|
||||||
|
__**New Contact Form Response**__
|
||||||
|
|
||||||
|
**From:** {form.get('name')} <{form.get('email')}>
|
||||||
|
|
||||||
|
''')
|
||||||
|
if form.get("message") == '':
|
||||||
|
discord_msg += '*No Message*'
|
||||||
|
else:
|
||||||
|
discord_msg += f'>>> {form.get("message")}'
|
||||||
|
discord_response = post(
|
||||||
|
url=discord_hook,
|
||||||
|
data={
|
||||||
|
'username': form.get('name'),
|
||||||
|
'content': discord_msg
|
||||||
|
}
|
||||||
|
).status_code
|
||||||
|
|
||||||
|
if discord_response == 204:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@app.route('/contact/', methods=('GET', 'POST'))
|
@app.route('/contact/', methods=('GET', 'POST'))
|
||||||
def contact():
|
def contact():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
discord_hook = environ['DISCORD_WEBHOOK']
|
if not validate_turnstile(request.form['cf-turnstile-response'], request.remote_addr):
|
||||||
print(discord_hook)
|
return render_template('contact.html', error=True, user_message='You appear to be a robot.')
|
||||||
print(request.form)
|
send_result = send_to_discord(request.form)
|
||||||
return redirect('/contact/')
|
if send_result:
|
||||||
|
return render_template('contact.html', user_message='Your message has been sent!')
|
||||||
|
return render_template('contact.html', error=True, user_message='An error occurred.')
|
||||||
else:
|
else:
|
||||||
return render_template('contact.html')
|
return render_template('contact.html')
|
||||||
|
@@ -3,3 +3,4 @@ flask-markdown>=0.3
|
|||||||
markdown>=3.4.1
|
markdown>=3.4.1
|
||||||
beautifulsoup4>=4.11.1
|
beautifulsoup4>=4.11.1
|
||||||
python-frontmatter>=1.1.0
|
python-frontmatter>=1.1.0
|
||||||
|
requests>=2.32.3
|
||||||
|
@@ -54,4 +54,9 @@
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#logo-container{
|
||||||
|
position: absolute;
|
||||||
|
height: 25vh;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -30,6 +30,7 @@ footer{
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo{
|
#logo{
|
||||||
@@ -167,4 +168,39 @@ a{
|
|||||||
|
|
||||||
.article-category:hover{
|
.article-category:hover{
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#contact-main{
|
||||||
|
padding: 20px 10px 0 10px;
|
||||||
|
min-height: 65vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#contact-main>h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, textarea{
|
||||||
|
display: block;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: #4c4c4c;
|
||||||
|
border-radius: 10px;
|
||||||
|
border:none;
|
||||||
|
color: white;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
label{
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf-turnstile{
|
||||||
|
width: fit-content;
|
||||||
|
padding: 10px 0 10px 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#contact-error{
|
||||||
|
color: red;
|
||||||
}
|
}
|
@@ -1,16 +1,20 @@
|
|||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
<main>
|
<main id="contact-main">
|
||||||
<h2>Contact Me</h2>
|
<h2>Contact Me</h2>
|
||||||
<p>Got a question or want to talk about something on this site? Drop me a message below:</p>
|
<p>Got a question or want to talk about something on this site? Drop me a message below:</p>
|
||||||
|
|
||||||
<form action="#" method="post">
|
<form action="#" method="post">
|
||||||
<label for="name">Name:</label>
|
<label for="name">Name:</label>
|
||||||
<input type="text" name="name">
|
<input type="text" name="name" required>
|
||||||
<label for="email">Email Address:</label>
|
<label for="email">Email Address:</label>
|
||||||
<input type="email" name="email">
|
<input type="email" name="email" required>
|
||||||
<label for="message">Message:</label>
|
<label for="message">Message:</label>
|
||||||
<textarea name="message"></textarea>
|
<textarea name="message" rows="10"></textarea>
|
||||||
|
<div class="cf-turnstile" data-sitekey="0x4AAAAAAA45FeR26JuvqKy7"></div>
|
||||||
<input type="submit" name="submit" value="Submit">
|
<input type="submit" name="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
|
{% if user_message is not none %}
|
||||||
|
<p id="{{ 'contact-error' if error else 'contact-message' }}">{{ user_message }}</p>
|
||||||
|
{% endif %}
|
||||||
</main>
|
</main>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
@@ -12,12 +12,14 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<script src="/static/js/filter_projects.js"></script>
|
<script src="/static/js/filter_projects.js"></script>
|
||||||
<script src="/static/js/update_copyright.js"></script>
|
<script src="/static/js/update_copyright.js"></script>
|
||||||
|
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<nav id="top-nav">
|
<nav id="top-nav">
|
||||||
<a href="/">About</a>
|
<a href="/">About</a>
|
||||||
<a href="/projects/">Projects</a>
|
<a href="/projects/">Projects</a>
|
||||||
|
<a href="/contact/">Contact</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="logo-container">
|
<div id="logo-container">
|
||||||
<div id="logo">
|
<div id="logo">
|
||||||
|
Reference in New Issue
Block a user