#!/usr/bin/env python3 # -*- coding: utf-8 -*- # /********************************************************************************** # * _author : Domeniko Gentner # * _mail : code@tuxstash.de # * _repo : https://git.tuxstash.de/gothseidank/labertasche # * _license : This project is under MIT License # *********************************************************************************/ from . import bp_jsconnector from flask import request, make_response, jsonify from flask_login import login_required from flask_cors import cross_origin from labertasche.database import labertasche_db as db from labertasche.helper import get_id_from_project_name from labertasche.models import TProjects, TComments, TEmail, TLocation from validators import url as validate_url from pathlib import Path from secrets import compare_digest import re def validate_project(project, is_edit=False): """ Validates important bits of a project database entry :param project: The json from the request, containing the data for a project. :param is_edit: If we are updating the database, we need to know, so we don't check for dupes on urls. :return: A response with the error or None if the project is valid. """ # Validate length if not len(project['name']) and \ not len(project['blogurl']) and \ not len(project['output']): return make_response(jsonify(status='too-short'), 400) # Validate project name if not re.match('^\\w+$', project['name']): return make_response(jsonify(status='invalid-project-name'), 400) # Check if project name already exists name_check = db.session.query(TProjects.name).filter(TProjects.name == project['name']).first() if name_check and not is_edit: return make_response(jsonify(status='project-exists'), 400) # Validate existing only if we are not editing url_exists = False if not is_edit: url_exists = db.session.query(TProjects.blogurl).filter(TProjects.blogurl == project['blogurl']).first() if not validate_url(project['blogurl']) or url_exists: return make_response(jsonify(status='invalid-blog-url'), 400) # Validate output path output = Path(project['output']).absolute() # The second check is needed, since javascript is passing an empty string instead of # null. For some reason, this makes SQLAlchemy accept the data and commit it to the db # without exception. This check prevents this issue from happening. if not output.exists() or len(project['output'].strip()) == 0: return make_response(jsonify(status='invalid-path-output'), 400) # Validate cache path, if caching is enabled if project['gravatar_cache']: cache = Path(project['gravatar_cache_dir']).absolute() if not cache.exists() or len(project['gravatar_cache_dir'].strip()) == 0: return make_response(jsonify(status='invalid-path-cache'), 400) return None @cross_origin() @bp_jsconnector.route("/project/new", methods=['POST']) @login_required def api_create_project(): """ Called on dashboard project overview to create a new project. :return: A string with an error code and 'ok' as string on success. """ response = validate_project(request.json) if response is not None: return response try: new_project = request.json # Remove trailing slash if compare_digest(new_project['blogurl'][-1], '/'): new_project['blogurl'] = new_project['blogurl'][:-1] db.session.add(TProjects(**new_project)) db.session.commit() except Exception as e: print(str(e)) db.session.rollback() return make_response(jsonify(status='exception', msg=str(e)), 500) return make_response(jsonify(status='ok'), 200) @cross_origin() @bp_jsconnector.route('/project/edit/', methods=['POST']) @login_required def api_edit_project_name(name: str): """ Renames the project. :param name: The previous name of the project to edit, must exist :return: A string with an error code and 'ok' as string on success. """ response = validate_project(request.json, is_edit=True) if response is not None: return response try: project = db.session.query(TProjects).filter(TProjects.name == name).first() project_json = request.json # Remove trailing slash to streamline it if compare_digest(project_json['blogurl'][-1], '/'): setattr(project, "blogurl", project_json['blogurl'][:-1].strip()) setattr(project, "id_project", project.id_project) setattr(project, "name", project_json['name']) setattr(project, "output", project_json['output'].strip()) setattr(project, "sendotp", project_json['sendotp']) setattr(project, "gravatar_cache", project_json['gravatar_cache']) setattr(project, "gravatar_cache_dir", project_json['gravatar_cache_dir']) setattr(project, "gravatar_size", project_json['gravatar_size']) setattr(project, "addon_smileys", project_json['addon_smileys']) db.session.commit() except Exception as e: print(str(e)) return make_response(jsonify(status='exception', msg=str(e)), 500) return make_response(jsonify(status='ok'), 200) @cross_origin() @bp_jsconnector.route('/project/delete/', methods=['GET']) @login_required def api_delete_project(name: str): """ Deletes a project from the database and all associated data :param name: The name of the project :return: A string with an error code and 'ok' as string on success. """ proj_id = get_id_from_project_name(name) if proj_id == -1: return make_response(jsonify(status='not-found'), 400) # noinspection PyBroadException try: db.session.query(TComments).filter(TComments.project_id == proj_id).delete() db.session.query(TLocation).filter(TLocation.project_id == proj_id).delete() db.session.query(TProjects).filter(TProjects.id_project == proj_id).delete() db.session.commit() db.session.flush() except Exception: return make_response(jsonify(status='exception'), 400) return make_response(jsonify(status='ok'), 200) @cross_origin() @bp_jsconnector.route('/project/exists/', methods=['GET']) @login_required def api_project_exists(name: str): proj_id = get_id_from_project_name(name) if proj_id == -1: return make_response(jsonify(status='not-found'), 200) return make_response(jsonify(status='ok'), 200) @cross_origin() @bp_jsconnector.route('/project/get/', methods=['GET']) @login_required def api_project_get_data(name: str): project = db.session.query(TProjects).filter(TProjects.name == name).first() if project: return make_response(jsonify(status='ok', id_project=project.id_project, name=project.name, blogurl=project.blogurl, output=project.output, sendotp=project.sendotp, gravatar_cache=project.gravatar_cache, gravatar_cache_dir=project.gravatar_cache_dir, gravatar_size=project.gravatar_size, addon_smileys=project.addon_smileys), 200) else: print('400') return make_response(jsonify(status='not-found'), 400)