first commit
This commit is contained in:
parent
ca7ca9af36
commit
4988092706
12
README.md
12
README.md
@ -1,2 +1,14 @@
|
|||||||
# contact-form
|
# contact-form
|
||||||
|
|
||||||
|
Formulaire de contacte pour le site de katzei
|
||||||
|
|
||||||
|
# installation
|
||||||
|
|
||||||
|
To install you need to:
|
||||||
|
|
||||||
|
* install flask, jinja2 an sqlalchemy
|
||||||
|
* clone the repo in the /site directory
|
||||||
|
* copy the etc content in /etc
|
||||||
|
* link /etc/contact-form/contact-form.service to /etc/systemd/system/contact-form.service
|
||||||
|
|
||||||
|
By default it will serve on port 9090
|
0
__init__.py
Normal file
0
__init__.py
Normal file
9
etc/contact-form/config.ini
Normal file
9
etc/contact-form/config.ini
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[app]
|
||||||
|
path = /dyn/contact
|
||||||
|
db_path = /tmp/contact-form.db
|
||||||
|
# time to keep the captacha valid in seconds
|
||||||
|
captcha_timout = 3600
|
||||||
|
|
||||||
|
[email]
|
||||||
|
reciever = email@example.com
|
||||||
|
server = localhost
|
13
etc/contact-form/contact-form.service
Normal file
13
etc/contact-form/contact-form.service
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Nextcloudregister a service to register in nextcloud
|
||||||
|
After=local-fs.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Environment=PYTHONPATH=/usr/lib/python3/dist-packages/:/sites/
|
||||||
|
ExecStart=/usr/bin/uwsgi-core --ini /etc/contact-form/uwsgi.ini
|
||||||
|
User=contactform
|
||||||
|
Group=contactform
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
5
etc/contact-form/uwsgi.ini
Normal file
5
etc/contact-form/uwsgi.ini
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[uwsgi]
|
||||||
|
socket = :9090
|
||||||
|
protocol = http
|
||||||
|
wsgi-file = /sites/contact-form/webapp.py
|
||||||
|
plugin = python3
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
flask
|
||||||
|
jinja2
|
||||||
|
sqlalchemy
|
58
templates/form.html
Normal file
58
templates/form.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="/css/frombulma.css">
|
||||||
|
<link rel="stylesheet" href="/css/styles.css">
|
||||||
|
<style type="text/css">
|
||||||
|
.form-line>.input, .form-line>.textarea{
|
||||||
|
border-color: #aaaaaa;
|
||||||
|
width:100%;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
.form-line>.textarea{
|
||||||
|
min-height: 25em;
|
||||||
|
}
|
||||||
|
.form-error{
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
.form-alert{
|
||||||
|
background-color: rgba(255, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="token" value={{token}}>
|
||||||
|
<div class="form-line field">
|
||||||
|
<label class="label" for=email>Adresse de courriel</label>
|
||||||
|
<div class="form-line control">
|
||||||
|
<input class="input" id="email" type="email" name="email" value="{{email}}" required/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-line field">
|
||||||
|
<label class="label" for="message">Votre message</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-line field">
|
||||||
|
<textarea class="textarea" id="message" name="message" rows="20" minlength="100" maxlength="10000" required>{{message}}</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-line field">
|
||||||
|
<label class="label" for="captcha">Vérification d’humanité: combien font {{first_number}} {{op_text}} {{second_number}} ?</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-line field">
|
||||||
|
<input class="input {% if captcha_error %}form-alert{% endif %}" id="captcha" type="number" name="captcha" min="-10" max="100" required />
|
||||||
|
</div>
|
||||||
|
{% if captcha_error %}
|
||||||
|
<div class="form-error">Le captcha est invalide veuillez réessayer</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-line field">
|
||||||
|
<input class="button is-link" type="submit" value="Envoyer" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
12
templates/success.html
Normal file
12
templates/success.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="/css/frombulma.css">
|
||||||
|
<link rel="stylesheet" href="/css/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Votre demande a bien été transmise. Nous vous recontacterons dès que possible.
|
||||||
|
</body>
|
||||||
|
</html>
|
109
webapp.py
Normal file
109
webapp.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
from configparser import ConfigParser
|
||||||
|
from email.message import EmailMessage
|
||||||
|
import random
|
||||||
|
import operator
|
||||||
|
from os.path import exists
|
||||||
|
from secrets import token_urlsafe
|
||||||
|
from smtplib import SMTP
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
import flask
|
||||||
|
import sqlalchemy
|
||||||
|
import sqlalchemy.orm
|
||||||
|
|
||||||
|
CONFIG_PATH = "/etc/contact-form/config.ini"
|
||||||
|
OPERATORS = (
|
||||||
|
('+', operator.add),
|
||||||
|
('×', operator.mul),
|
||||||
|
('-', operator.sub),
|
||||||
|
)
|
||||||
|
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read(CONFIG_PATH)
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
Base = sqlalchemy.orm.declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Captcha(Base):
|
||||||
|
__tablename__ = "captcha"
|
||||||
|
|
||||||
|
token = sqlalchemy.Column(sqlalchemy.String(90), primary_key=True)
|
||||||
|
answer = sqlalchemy.Column(sqlalchemy.String(3))
|
||||||
|
expiration = sqlalchemy.Column(sqlalchemy.Integer())
|
||||||
|
|
||||||
|
|
||||||
|
def get_session():
|
||||||
|
db_path = config.get("app", "db_path", fallback="")
|
||||||
|
engine = sqlalchemy.create_engine(f"sqlite:///{db_path}")
|
||||||
|
if not exists(db_path):
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
session_factory = sqlalchemy.orm.sessionmaker(engine)
|
||||||
|
return session_factory()
|
||||||
|
|
||||||
|
|
||||||
|
def clean_db(session):
|
||||||
|
session.query(Captcha).filter(Captcha.expiration < int(time())).delete()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_captacha():
|
||||||
|
session = get_session()
|
||||||
|
clean_db(session)
|
||||||
|
first_number = random.randrange(10)
|
||||||
|
second_number = random.randrange(10)
|
||||||
|
op_text, op_func = random.choice(OPERATORS)
|
||||||
|
result = op_func(first_number, second_number)
|
||||||
|
captcha = Captcha(
|
||||||
|
token=token_urlsafe(67),
|
||||||
|
answer=str(result),
|
||||||
|
expiration=int(time()) + config.getint("app", "captcha_timout", fallback=3600),
|
||||||
|
)
|
||||||
|
session.add(captcha)
|
||||||
|
session.commit()
|
||||||
|
return {
|
||||||
|
"first_number": first_number,
|
||||||
|
"second_number": second_number,
|
||||||
|
"op_text": op_text,
|
||||||
|
"token": captcha.token,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_captcha(token, value):
|
||||||
|
result = False
|
||||||
|
session = get_session()
|
||||||
|
clean_db(session)
|
||||||
|
captcha = session.query(Captcha).filter(Captcha.token == token).one_or_none()
|
||||||
|
if captcha:
|
||||||
|
result = value == captcha.answer
|
||||||
|
session.query(Captcha).filter(Captcha.token == token).delete()
|
||||||
|
session.commit()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def send_email(sender, message):
|
||||||
|
email = EmailMessage()
|
||||||
|
email.set_content(message)
|
||||||
|
email["From"] = sender
|
||||||
|
email["To"] = config.get("email", "reciever")
|
||||||
|
smtp = SMTP(config.get("email", "server"))
|
||||||
|
smtp.send_message(email)
|
||||||
|
smtp.quit()
|
||||||
|
|
||||||
|
@app.route(config.get("app", "path"), methods=["GET"])
|
||||||
|
def get_form(context=None):
|
||||||
|
context = (context or {}) | generate_captacha()
|
||||||
|
return flask.render_template("form.html", **context)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(config.get("app", "path"), methods=["POST"])
|
||||||
|
def validate_form():
|
||||||
|
form = flask.request.form
|
||||||
|
if not all(form.get(f, False) for f in ("email", "message", "captcha", "token")):
|
||||||
|
return "Erreur", 400
|
||||||
|
if not validate_captcha(form["token"], form["captcha"]):
|
||||||
|
context = dict(**form)
|
||||||
|
context["captcha_error"] = True
|
||||||
|
context.pop("captcha", None)
|
||||||
|
return get_form(context=context)
|
||||||
|
send_email(sender=form["email"], message=form["message"])
|
||||||
|
return flask.render_template("success.html")
|
Loading…
Reference in New Issue
Block a user