first commit
This commit is contained in:
		
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,2 +1,14 @@ | ||||
| # 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") | ||||
		Reference in New Issue
	
	Block a user
	 Meewan
					Meewan