summaryrefslogtreecommitdiff
path: root/webapp/pogojig.py
diff options
context:
space:
mode:
authorjaseg <git@jaseg.net>2019-09-28 13:53:02 +0200
committerjaseg <git@jaseg.net>2019-09-28 13:53:02 +0200
commit031b6f736187efdc64c525a40c687698701e73e1 (patch)
tree140a96a98822effda864d77720a227aae928f5f9 /webapp/pogojig.py
parent6002d409143a6726899a4de15c3a6b279a6b1d71 (diff)
downloadpogojig-031b6f736187efdc64c525a40c687698701e73e1.tar.gz
pogojig-031b6f736187efdc64c525a40c687698701e73e1.tar.bz2
pogojig-031b6f736187efdc64c525a40c687698701e73e1.zip
Initial webapp design
Diffstat (limited to 'webapp/pogojig.py')
-rw-r--r--webapp/pogojig.py111
1 files changed, 111 insertions, 0 deletions
diff --git a/webapp/pogojig.py b/webapp/pogojig.py
new file mode 100644
index 0000000..a7201d0
--- /dev/null
+++ b/webapp/pogojig.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+
+# TODO setup webserver user disk quota
+
+import tempfile
+import uuid
+from functools import wraps
+from os import path
+import os
+import sqlite3
+
+from flask import Flask, url_for, redirect, session, make_response, render_template, request, send_file, abort, flash
+from flask_wtf import FlaskForm
+from flask_wtf.file import FileField, FileRequired
+from wtforms.fields import RadioField
+from wtforms.validators import DataRequired
+from werkzeug.utils import secure_filename
+
+from job_queue import JobQueue
+
+app = Flask(__name__, static_url_path='/static')
+app.config.from_envvar('POGOJIG_SETTINGS')
+
+class UploadForm(FlaskForm):
+ upload_file = FileField(validators=[DataRequired()])
+
+class ResetForm(FlaskForm):
+ pass
+
+job_queue = JobQueue(app.config['JOB_QUEUE_DB'])
+
+def tempfile_path(namespace):
+ """ Return a path for a per-session temporary file identified by the given namespace. Create the session tempfile
+ dir if necessary. The application tempfile dir is controlled via the upload_path config value and not managed by
+ this function. """
+ sess_tmp = path.join(app.config['UPLOAD_PATH'], session['session_id'])
+ os.makedirs(sess_tmp, exist_ok=True)
+ return path.join(sess_tmp, namespace)
+
+def require_session_id(fun):
+ @wraps(fun)
+ def wrapper(*args, **kwargs):
+ if 'session_id' not in session:
+ session['session_id'] = str(uuid.uuid4())
+ return fun(*args, **kwargs)
+ return wrapper
+
+@app.route('/')
+def index():
+ return app.send_static_file('index.html')
+
+@app.route('/jigerator')
+@require_session_id
+def jigerator():
+ forms = {
+ 'svg_form': UploadForm(),
+ 'reset_form': ResetForm() }
+
+ if 'render_job' in session:
+ job = job_queue[session['render_job']]
+ if job.finished:
+ if job.result != 0:
+ flash(f'Error processing SVG file', 'success') # FIXME make this an error, add CSS
+ del session['render_job']
+
+ r = make_response(render_template('jigerator.html', has_renders=path.isfile(tempfile_path('output.zip')), **forms))
+ if 'render_job' in session:
+ r.headers.set('refresh', '10')
+ return r
+
+# NOTES about the SVG file upload routines
+# * The maximum upload size is limited by the MAX_CONTENT_LENGTH config setting.
+# * The uploaded files are deleted after a while by systemd tmpfiles.d
+# TODO: validate this setting applies *after* gzip transport compression
+
+def render():
+ if 'render_job' in session:
+ job_queue[session['render_job']].abort()
+ session['render_job'] = job_queue.enqueue('render',
+ session_id=session['session_id'],
+ client=request.remote_addr)
+
+@app.route('/upload/svg', methods=['POST'])
+@require_session_id
+def upload_svg():
+ upload_form = UploadForm()
+ if upload_form.validate_on_submit():
+ f = upload_form.upload_file.data
+ f.save(tempfile_path('input.svg'))
+ session['filename'] = secure_filename(f.filename) # Cache filename for later download
+
+ render()
+ flash(f'SVG file successfully uploaded.', 'success')
+ return redirect(url_for('jigerator'))
+
+@app.route('/render/download')
+def render_download():
+ return send_file(tempfile_path(f'renders.zip'),
+ mimetype='application/zip',
+ as_attachment=True,
+ attachment_filename=f'{path.splitext(session["filename"])[0]}_pogojig.zip')
+
+@app.route('/session_reset', methods=['POST'])
+@require_session_id
+def session_reset():
+ if 'render_job' in session:
+ job_queue[session['render_job']].abort()
+ session.clear()
+ flash('Session reset', 'success');
+ return redirect(url_for('jigerator'))
+