Files
littleprynter/src/main.py

184 lines
6.1 KiB
Python

# 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"))