From 83ea3a7a64b2df2ee0c8f6d3cff0d1ceb0857565 Mon Sep 17 00:00:00 2001 From: Tobias Weise Date: Wed, 11 Sep 2024 20:58:03 +0200 Subject: [PATCH] registration email added --- .../workflows/deploy_via_docker_compose.yml | 4 + backend/app.py | 85 +++++++++++++------ backend/lib/elastictools.py | 14 +-- backend/lib/mail.py | 31 +++++-- backend/lib/models.py | 5 +- backend/lib/user.py | 2 +- backend/public/index.html | 8 +- backend/public/main.js | 1 + backend/requirements.txt | 1 + backend/templates/html_mail.twig | 41 +++++++++ deployment/docker-compose.yml | 10 +-- deployment/sample.env | 6 ++ 12 files changed, 156 insertions(+), 52 deletions(-) create mode 100644 backend/templates/html_mail.twig diff --git a/.gitea/workflows/deploy_via_docker_compose.yml b/.gitea/workflows/deploy_via_docker_compose.yml index 518ab47..d423851 100644 --- a/.gitea/workflows/deploy_via_docker_compose.yml +++ b/.gitea/workflows/deploy_via_docker_compose.yml @@ -21,6 +21,10 @@ jobs: git pull echo "============ Git pull finished! =========" echo '${{ vars.ENV }}' > .env + echo '\n' >> .env + echo '${{ secrets.CB_EMAIL_SMTP }}\n' >> .env + echo '${{ secrets.CB_EMAIL_ADDR }}\n' >> .env + echo '${{ secrets.CB_EMAIL_PWD }}\n' >> .env echo "============ Env creation finished! =========" diff --git a/backend/app.py b/backend/app.py index 655d8eb..89508a7 100644 --- a/backend/app.py +++ b/backend/app.py @@ -19,7 +19,6 @@ from flask import Flask, send_from_directory, send_file, Response, request, json from flask_openapi3 import Info, Tag, OpenAPI, Server #FileStorage from flask_socketio import SocketIO, join_room, leave_room, rooms, send from werkzeug.utils import secure_filename - import asyncio import logging @@ -133,6 +132,9 @@ def uses_jwt(logger=None, required=True): def create_app(): + app_name = "Creative Bots" + + # JWT Bearer Sample jwt = { "type": "http", @@ -144,7 +146,7 @@ def create_app(): info = Info( - title="CreativeBots-API", + title=app_name + " API", version=__version__, summary="The REST-API to manage bots, users and more!", description="" @@ -192,13 +194,10 @@ def create_app(): @socket.on('client message') def handle_message(message): - SocketMessage.model_validate(message) logger.info("Starting stream") - - #try: room = message["room"] question = message["question"] system_prompt = message["system_prompt"] @@ -305,12 +304,20 @@ def create_app(): case [user]: if user["password_hash"] == hash_password(form.password + form.email): - token = pyjwt.encode({"email": form.email}, jwt_secret, algorithm="HS256") - #logger.info(token) - return jsonify({ - 'status': 'success', - 'jwt': token - }) + if not user["isEmailVerified"]: + msg = "E-Mail unverified!" + logger.error(msg) + return jsonify({ + 'status': 'error', + 'message': msg + }), 400 + else: + token = pyjwt.encode({"email": form.email}, jwt_secret, algorithm="HS256") + #logger.info(token) + return jsonify({ + 'status': 'success', + 'jwt': token + }) else: msg = "Invalid password!" logger.error(msg) @@ -341,12 +348,15 @@ def create_app(): if User.get(id=form.email, ignore=404) is not None: + msg = "User with that e-mail address already exists!" + logger.error(msg) return jsonify({ 'status': 'error', - "message": "User with that e-mail address already exists!" + "message": msg }) else: + logger.info("Try creating user...!") user = User(meta={'id': form.email}) user.creation_date = datetime.now() user.email = form.email @@ -354,21 +364,23 @@ def create_app(): user.role = "User" user.isEmailVerified = False user.save() + logger.info("User created!") - msg = """ + msg = f"""

Verify E-Mail

- Hi! - Please click on the following link to verify your e-mail: - - Click here! + Click here! """ - send_mail(user.email, "User registration @ Creative Bots", "Creative Bots", msg) - + send_mail( + target_mail=user.email, + subject="User registration @ " + app_name, + msg=msg + ) + logger.info("Mail send!") return jsonify({ 'status': 'success' }) @@ -599,9 +611,6 @@ def create_app(): "score_docs": xs }) - - - #-----------------Embedding---------------------- class TrainTextRequest(BaseModel): @@ -659,15 +668,41 @@ def create_app(): d = {} d["module_versions"] = get_module_versions() - d["OLLAMA_NUM_PARALLEL"] = os.getenv("OLLAMA_NUM_PARALLEL") - d["OLLAMA_MAX_LOADED_MODELS"] = os.getenv("OLLAMA_MAX_LOADED_MODELS") + #d["OLLAMA_NUM_PARALLEL"] = os.getenv("OLLAMA_NUM_PARALLEL") + #d["OLLAMA_MAX_LOADED_MODELS"] = os.getenv("OLLAMA_MAX_LOADED_MODELS") d["cpus"] = multiprocessing.cpu_count() #return "CPUs: " + str(cpus) + "
" + json.dumps(get_module_versions(), indent=4).replace("\n", "
") - return json.dumps(d, indent=4).replace("\n", "
") + @app.route("/verify", methods=['GET']) + def verify_email(): + x = request.args.get('id') + s = User.search() + s = s.filter('term', isEmailVerified=False).query('match', password_hash=x) + results = s.execute() + + # when you execute the search the results are wrapped in your document class (Post) + for user in results: + #print(post.meta.score, post.title) + if user.password_hash == x: + user.isEmailVerified = True + user.save() + return "E-Mail verified!" + + + return "Verrification Error!" + """ + match get_by_id(index="user", id_field_name="password_hash", id_value=x): + case [d]: + #user.isEmailVerified = True + #user.save() + return "E-Mail verified!" + + case _: + return "Verrification Error!" + """ @app.route('/') #generische Route (auch Unterordner) diff --git a/backend/lib/elastictools.py b/backend/lib/elastictools.py index d4c6399..33087be 100644 --- a/backend/lib/elastictools.py +++ b/backend/lib/elastictools.py @@ -9,8 +9,7 @@ from elasticsearch.exceptions import ConnectionError def get_by_id(index: str, id_field_name: str, id_value: str): - client = connections.get_connection() - response = Search(using=client, index=index).filter("term", **{id_field_name: id_value})[0:10000].execute() + response = Search(using=connections.get_connection(), index=index).filter("term", **{id_field_name: id_value})[0:10000].execute() return [hit.to_dict() for hit in response] @@ -30,15 +29,13 @@ def update_by_id(index: str, id_field_name: str, id_value: str, values_to_set: D def delete_by_id(index: str, id_field_name: str, id_value: str): - client = connections.get_connection() - s = Search(using=client, index=index).filter("term", **{id_field_name: id_value}) + s = Search(using=connections.get_connection(), index=index).filter("term", **{id_field_name: id_value}) response = s.delete() #if not response.success(): # raise Exception("Unable to delete id '%s' in index '%' !" % (index, id_value)) print(response, flush=True) - def get_datetime_interval(search: Search, start, end) -> Search: return search.filter("range", timest={"gte": start}).filter("range", timest={"lte": end}) @@ -57,21 +54,18 @@ def simplify_properties(d): def get_type_schema(): - client = connections.get_connection() - d = client.indices.get(index="*").body + d = connections.get_connection().indices.get(index="*").body new_d = {} for index, d2 in d.items(): new_d[index] = simplify_properties(d2["mappings"]) return new_d - def wait_for_elasticsearch(): i = 1 while True: try: - client = connections.get_connection() - client.indices.get_alias(index="*") + connections.get_connection().indices.get_alias(index="*") print("Elasticsearch found! Run Flask-app!", flush=True) return except ConnectionError: diff --git a/backend/lib/mail.py b/backend/lib/mail.py index bab70d1..5865498 100644 --- a/backend/lib/mail.py +++ b/backend/lib/mail.py @@ -1,14 +1,33 @@ +import os +from jinja2 import Environment, FileSystemLoader from smtplib import * -from email.mime.text import MIMEText +from email.message import EmailMessage -def send_mail(target_mail, subject, sender_mail, msg): +env = Environment(loader=FileSystemLoader('templates')) - msg = MIMEText(msg) +# Credentials +username = os.getenv("EMAIL_ADDR") +password = os.getenv("EMAIL_PWD") +smtp_domain_and_port = os.getenv("EMAIL_SMTP") + +assert username +assert password + +def send_mail(target_mail, subject, msg, sender_mail=username): + html = env.get_template('html_mail.twig').render( + subject=subject, + msg=msg + ) + + msg = EmailMessage() msg['Subject'] = subject msg['From'] = sender_mail msg['To'] = target_mail + msg.set_content(html, subtype='html') - smtp = SMTP('mailserver', port=10025) - smtp.sendmail("Creative Bots", [target_mail], msg.as_string()) - smtp.quit() + domain, port = smtp_domain_and_port.split(":") + #with SMTP_SSL('smtp.gmx.de', port=465) as smtp: + with SMTP_SSL(domain, port=int(port)) as smtp: + smtp.login(username, password) + smtp.send_message(msg) diff --git a/backend/lib/models.py b/backend/lib/models.py index 921824d..3fef1ef 100644 --- a/backend/lib/models.py +++ b/backend/lib/models.py @@ -4,7 +4,10 @@ from elasticsearch_dsl import Document, InnerDoc, Nested, Date, Integer, Keyword class User(Document): creation_date = Date() email = Keyword() - password_hash = Text(index=False) + #password_hash = Text(index=False) + password_hash = Keyword() + #password_hash = Text(index=True) + role = Keyword() #salt = Text(index=False) diff --git a/backend/lib/user.py b/backend/lib/user.py index 10b9414..f5b8bb8 100644 --- a/backend/lib/user.py +++ b/backend/lib/user.py @@ -2,6 +2,7 @@ All around managing users """ import os, json, hashlib, traceback, logging +from datetime import datetime, date from elasticsearch import NotFoundError, Elasticsearch # for normal read/write without vectors from lib.models import User @@ -15,7 +16,6 @@ assert elastic_url def hash_password(s: str) -> str: return hashlib.md5(s.encode('utf-8')).hexdigest() - def create_user(email, password, role="user", verified=False): user = User(meta={'id': email}, email=email, password_hash=hash_password(password + email), role=role) user.creation_date = datetime.now() diff --git a/backend/public/index.html b/backend/public/index.html index 32dbe0a..f00b931 100644 --- a/backend/public/index.html +++ b/backend/public/index.html @@ -260,7 +260,7 @@
- @ + @@ -270,7 +270,7 @@
@@ -459,6 +459,8 @@
+ + + diff --git a/backend/public/main.js b/backend/public/main.js index faa3d9e..a466fea 100755 --- a/backend/public/main.js +++ b/backend/public/main.js @@ -684,6 +684,7 @@ window.onload = async ()=>{ } catch(e){ + console.error(e); console.error("Registration failed!"); } }; diff --git a/backend/requirements.txt b/backend/requirements.txt index 45091a1..bec0fa1 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -3,6 +3,7 @@ webdriver_manager requests==2.32.3 selenium==4.23.1 bs4 +jinja2 elasticsearch==8.15.0 elasticsearch-dsl==8.15.1 diff --git a/backend/templates/html_mail.twig b/backend/templates/html_mail.twig new file mode 100644 index 0000000..14631bf --- /dev/null +++ b/backend/templates/html_mail.twig @@ -0,0 +1,41 @@ + + + + {{ subject }} + + + + + + + + +
+ + + +
+

{{ subject }}

+
+ + + + +
+

+ {{ msg }} +

+
+
+ + + + diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml index 406fad2..f779acc 100644 --- a/deployment/docker-compose.yml +++ b/deployment/docker-compose.yml @@ -19,6 +19,7 @@ services: minio: container_name: ${APP_PREFIX}_minio image: minio/minio + restart: always ports: - "29000:9000" - "29001:9001" @@ -37,7 +38,8 @@ services: image: neo4j #image: neo4j:3.5 #image: neo4j:4.1 - restart: unless-stopped + #restart: unless-stopped + restart: always ports: - 7474:7474 - 7687:7687 @@ -104,12 +106,6 @@ services: - /dev/dri - - - - - - #ollama-webui: # container_name: ${APP_PREFIX}_ollama-webui # image: ghcr.io/ollama-webui/ollama-webui:main diff --git a/deployment/sample.env b/deployment/sample.env index 211c979..e28cece 100644 --- a/deployment/sample.env +++ b/deployment/sample.env @@ -9,3 +9,9 @@ SECRET=23A344F670E #WARN INFO FATAL LOG_LEVEL=WARN + +#Admin E-Mail sender account +EMAIL_SMTP=smtp.mail.net:465 +EMAIL_ADDR=user@mail.net +EMAIL_PWD=somepass +