# Welcome to the LittlePrynter's source code. # This program expose a web interface, with user authentification, that makes it possible to print messages from the web. # It also exposes a API, making it possible to print and interface with much of the printer's abilities. # We first define the connection to the printer itself, # Then we build the API around Flask, # Then we build the web interface, using the simple Jinja2 templating. # Following are the librairies we import, from flask import Flask, request, render_template, flash, abort, redirect, url_for, make_response, jsonify # Used for the web framework from flask_limiter import Limiter from flask_limiter.util import get_remote_address from printer import Printer # The wrapper for the printer class from web import Web # Wrapper for the web routes and API import toml # Used for the config file parsing import pprint # To pretty print JSON import time # To sleep import os # For VARS from the shell. # Variables app = Flask(__name__) # Load the configuration file try: app.logger.debug("Loading config file...") configuration_file = toml.load("configuration/config.toml") except TypeError : app.logger.error("Unable to load the config file: invalid type or is a list containing invalid types") exit(-1) except toml.TomlDecodeError: app.logger.error("An error occured while decoding the file") exit(-1) except Exception as e: app.logger.error("Error while loading file : " + str(e)) exit(-1) app.logger.debug("Config file loaded !") # Define the USB connections here. vendor_id = configuration_file["printer"]["vendor_id"] device_id = configuration_file["printer"]["device_id"] UPLOAD_FOLDER = str(configuration_file["printer"]["upload_folder"]) ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} # Output the config file if os.getenv('LIPY_DEBUG') == True: pprint.pprint(configuration_file) # We define the app module used by Flask app.secret_key = configuration_file["secrets"]["flask_secret_key"] app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['ALLOWED_EXTENSIONS'] = ALLOWED_EXTENSIONS app.config['MAX_CONTENT_LENGTH'] = 3 * 1000 * 1000 # Maximum 3Mb for a file upload app.config['TEMPLATES_AUTO_RELOAD'] = True # Printer connection # Uses the class defined in the printer.py file printer = Printer(app,0x04b8, 0x0e28) printer.init_printer() # Web routes web = Web(app, printer) limiter = Limiter( app, key_func=get_remote_address, default_limits=["1500 per day", "500 per hour"] ) @app.route('/') @limiter.limit("1/second", override_defaults=False) def index(): app.logger.debug("Loading index") return render_template('index.html') @app.route('/webcam') @limiter.limit("1/second", override_defaults=False) def webcam(): app.logger.debug("Loading webcam interface") return render_template('webcam.html') # API routes # The api has the following methods # api/print/{sms,img,letter,qr,barcode} # api/auth/{login,logout} # api/status/{paper,ping,stats} # If you just call the api route, you get a help back. @app.route('/api') @app.route('/api/print') @limiter.limit("1/second", override_defaults=False) def api_index(): app.logger.debug("Loading API") return render_template("api.html") @app.route('/api/print/sms', methods=['POST']) @limiter.limit("2/minute", override_defaults=False) def api_print_sms(): app.logger.debug("Printing an sms") try: txt = request.form["txt"] sign = request.form["signature"] except Exception as e: flash(e,'error') redirect(url_for('index')) try: web.print_sms(txt,sign) except Exception as e: pass return redirect(url_for('index')) @app.route('/api/print/img', methods=['POST']) @limiter.limit("2/minute", override_defaults=False) def api_print_image(): app.logger.debug("Printing an image") try: sign = request.form["signature"] except Exception as e: flash(str(e),'error') app.logger.error(str(e) + " - Whoops, no forms submitted or missing signature.") return redirect(url_for('index')) if request.method == 'POST': # check if the post request has the file part try: if 'img' not in request.files: flash('No file found. Did you use the good form ?', 'error') app.logger.error("No file found. Did you use the good form ?") return redirect(url_for("index")) else: file = request.files['img'] except Exception as e: if sign is not None and photo is not None: pass else: flash(str(e), 'error') app.logger.error("Couldn't get an image nor signature : " + str(e)) # If the user does not select a file, the browser submits an # empty file without a filename. if file.filename == '': app.logger.error("Submitted file has no filename !") flash('No file submitted, please select a file','error') return redirect(url_for("index")) try: app.logger.debug("Sending the image to the printer.") web.print_image(file, sign) except Exception as e: pass else: flash('Cannot access to page with this method.','error') app.logger.debug('Bad access type to this API.') return redirect(url_for("index")) @app.route('/login') @limiter.limit("1/second", override_defaults=False) def login_page(): # web.login(username,password) return redirect(url_for("index")) @app.route('/logout') @limiter.limit("1/second", override_defaults=False) def logout_page(): # web.logout(username, password) return redirect(url_for("index")) @app.errorhandler(429) def ratelimit_handler(e): flash("Rate limit reached, please slow down :) ( Currently at "+ e.description + ")", 'error') return redirect(url_for("index")) @app.route("/ping") @limiter.exempt def ping(): flash("🏓 Pong !",'info') return redirect(url_for("index"))