-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
108 lines (88 loc) · 3.14 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/python3
'''
Flask wsgi-interface for mdTeXerPad
Base file of mdTeXerPad:
A pad-like browser app to live-render Markdown with TeX-formulas
This starts a Webserver and a WebSocketServer for the mdTeXerPad.
'''
import os
import logging
from contextvars import ContextVar
from flask import Flask, render_template, request, send_from_directory, make_response, jsonify
from flask_sockets import Sockets # Not working on Ubuntu 20.04 due to an old version of gevent
import asyncio
import json
import mdtex2html
# global settings:
app = Flask(__name__)
debug = True
if __name__ != '__main__':
gunicorn_logger = logging.getLogger('gunicorn.error')
app.logger.handlers = gunicorn_logger.handlers
app.logger.setLevel(gunicorn_logger.level)
sockets = Sockets(app)
host='0.0.0.0'
extensions=['def_list', 'fenced_code', 'md_in_html', 'tables', 'admonition', 'nl2br', 'sane_lists', 'toc']
# WebServer stuff:
@app.route('/', defaults={'path': ''}, methods=['GET'])
@app.route('/<path:path>', methods=['GET'])
def index(path):
app.logger.info('http connection: '+str(request.remote_addr))
return render_template('page.html', path=path)
@app.route('/static/<path:path>', methods=['GET'])
def sendStatic(path):
return send_from_directory('', path)
# WebSocketServer-stuff:
# Only 1 gunicorn-worker is supported since the connections are stored
# in variables that the other workers can't access!
USERS = set()
mdtex = type('', (), {})() # empty object, so that it is available in all async functions
mdtex.value = '''
# Example-Title
TeX-Formula: $\sqrt2=x^2 \Rightarrow x=\sqrt{\sqrt{2}}$
- This
- is
- a List
- with an image 
Delete this and write your own `mdTeX`!
'''
@sockets.route('/ws')
def mdtex_socket(ws):
if ws not in USERS:
USERS.add(ws)
app.logger.info('ws connected: '+str(request.remote_addr)+', '+str(len(USERS))+' users online now')
html = mdtex2html.convert(mdtex.value, extensions)
replyd = {'mdtex': mdtex.value, 'html': html, 'users': str(len(USERS))}
replyj = json.dumps(replyd)
ws.send(replyj)
message = None
while True:
if message != None:
mdtex.value = str(message)
html = mdtex2html.convert(mdtex.value, extensions)
replyd = {'mdtex': mdtex.value, 'html': html, 'users': str(len(USERS))}
replyj = json.dumps(replyd)
if message != None:
asyncio.run(sendUpdate())
try:
message = ws.receive()
except:
# close connection on error, client may reconnect
break
async def sendUpdate():
html = mdtex2html.convert(mdtex.value, extensions)
replyd = {'mdtex': mdtex.value, 'html': html, 'users': str(len(USERS))}
replyj = json.dumps(replyd)
for ws in USERS.copy():
try:
ws.send(replyj)
except:
try:
USERS.remove(ws)
except WebSocketError:
pass
# garbage collector will remove dead object
app.logger.info('One ws-client was dropped, '+str(len(USERS))+' users remain')
# run it:
if __name__ == '__main__':
app.run(host=host, debug=debug)