""" Onlylegs - API endpoints Used intermally by the frontend and possibly by other applications """ from uuid import uuid4 import os import io import logging from flask import ( Blueprint, current_app, send_from_directory, send_file, request, g, abort, flash, jsonify) from werkzeug.utils import secure_filename from PIL import Image, ImageOps # ImageFilter from sqlalchemy.orm import sessionmaker from gallery.auth import login_required from . import db # Import db to create a session from . import metadata as mt blueprint = Blueprint('api', __name__, url_prefix='/api') db_session = sessionmaker(bind=db.engine) db_session = db_session() @blueprint.route('/uploads/', methods=['GET']) def uploads(file): """ Returns a file from the uploads folder w and h are the width and height of the image for resizing f is whether to apply filters to the image, such as blurring NSFW images """ # Get args width = request.args.get('w', default=0, type=int) # Width of image height = request.args.get('h', default=0, type=int) # Height of image filtered = request.args.get('f', default=False, type=bool) # Whether to apply filters # if no args are passed, return the raw file if width == 0 and height == 0 and not filtered: if not os.path.exists(os.path.join(current_app.config['UPLOAD_FOLDER'], secure_filename(file))): abort(404) return send_from_directory(current_app.config['UPLOAD_FOLDER'], file ,as_attachment=True) # Of either width or height is 0, set it to the other value to keep aspect ratio if width > 0 and height == 0: height = width elif width == 0 and height > 0: width = height set_ext = current_app.config['ALLOWED_EXTENSIONS'] buff = io.BytesIO() # Open image and set extension try: img = Image.open(os.path.join(current_app.config['UPLOAD_FOLDER'],file)) except FileNotFoundError: logging.error('File not found: %s, possibly broken upload', file) abort(404) except Exception as err: logging.error('Error opening image: %s', err) abort(500) img_ext = os.path.splitext(file)[-1].lower().replace('.', '') img_ext = set_ext[img_ext] # Get ICC profile as it alters colours when saving img_icc = img.info.get("icc_profile") # Resize image and orientate correctly img.thumbnail((width, height), Image.LANCZOS) img = ImageOps.exif_transpose(img) # If has NSFW tag, blur image, etc. if filtered: #img = img.filter(ImageFilter.GaussianBlur(20)) pass try: img.save(buff, img_ext, icc_profile=img_icc) except OSError: # This usually happens when saving a JPEG with an ICC profile # Convert to RGB and try again img = img.convert('RGB') img.save(buff, img_ext, icc_profile=img_icc) except Exception as err: logging.error('Could not resize image %s, error: %s', file, err) abort(500) img.close() # Seek to beginning of buffer and return buff.seek(0) return send_file(buff, mimetype='image/' + img_ext) @blueprint.route('/upload', methods=['POST']) @login_required def upload(): """ Uploads an image to the server and saves it to the database """ form_file = request.files['file'] form = request.form if not form_file: return abort(404) img_ext = os.path.splitext(form_file.filename)[-1].replace('.', '').lower() img_name = f"GWAGWA_{str(uuid4())}.{img_ext}" if not img_ext in current_app.config['ALLOWED_EXTENSIONS'].keys(): logging.info('File extension not allowed: %s', img_ext) abort(403) if os.path.isdir(current_app.config['UPLOAD_FOLDER']) is False: os.mkdir(current_app.config['UPLOAD_FOLDER']) # Save to database try: db_session.add(db.posts(img_name, form['description'], form['alt'], g.user.id)) db_session.commit() except Exception as err: logging.error('Could not save to database: %s', err) abort(500) # Save file try: form_file.save( os.path.join(current_app.config['UPLOAD_FOLDER'], img_name)) except Exception as err: logging.error('Could not save file: %s', err) abort(500) return 'Gwa Gwa' @blueprint.route('/remove/', methods=['POST']) @login_required def remove(img_id): """ Deletes an image from the server and database """ img = db_session.query(db.posts).filter_by(id=img_id).first() if img is None: abort(404) if img.author_id != g.user.id: abort(403) try: os.remove(os.path.join(current_app.config['UPLOAD_FOLDER'],img.file_name)) except FileNotFoundError: # File was already deleted or doesn't exist logging.warning('File not found: %s, already deleted or never existed', img.file_name) except Exception as err: logging.error('Could not remove file: %s', err) abort(500) try: db_session.query(db.posts).filter_by(id=img_id).delete() db_session.commit() except Exception as err: logging.error('Could not remove from database: %s', err) abort(500) logging.info('Removed image (%s) %s', img_id, img.file_name) flash(['Image was all in Le Head!', 1]) return 'Gwa Gwa' @blueprint.route('/metadata/', methods=['GET']) def metadata(img_id): """ Yoinks metadata from an image """ img = db_session.query(db.posts).filter_by(id=img_id).first() if img is None: abort(404) img_path = os.path.join(current_app.config['UPLOAD_FOLDER'], img.file_name) exif = mt.Metadata(img_path).yoink() return jsonify(exif) @blueprint.route('/logfile') @login_required def logfile(): """ Gets the log file and returns it as a JSON object """ filename = logging.getLoggerClass().root.handlers[0].baseFilename log_dict = {} i = 0 with open(filename, encoding='utf-8') as file: for line in file: line = line.split(' : ') event = line[0].strip().split(' ') event_data = { 'date': event[0], 'time': event[1], 'severity': event[2], 'owner': event[3] } message = line[1].strip() try: message_data = { 'code': int(message[1:4]), 'message': message[5:].strip() } except ValueError: message_data = {'code': 0, 'message': message} except Exception as err: logging.error('Could not parse log file: %s', err) abort(500) log_dict[i] = {'event': event_data, 'message': message_data} i += 1 # Line number, starts at 0 return jsonify(log_dict)