-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
205 lines (154 loc) · 5.37 KB
/
Copy pathapp.py
File metadata and controls
205 lines (154 loc) · 5.37 KB
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import json
import time
import jwt
import requests
from flask import Flask, request, redirect, g, session, url_for, render_template
from jwt import PyJWKClient
from peewee import SqliteDatabase, Model, CharField, TextField, IntegrityError
import config
app = Flask(__name__)
app.config.from_object(config)
jwk_client = PyJWKClient("https://usetrmnl.com/.well-known/jwks.json")
database = SqliteDatabase(app.config["DATABASE"])
class BaseModel(Model):
class Meta:
database = database
class PluginInstall(BaseModel):
plugin_setting_id = CharField(null=True)
plugin_uuid = CharField(null=True)
access_token = CharField(null=True)
data = TextField(null=True)
@app.route("/new")
def new():
# new plugin install endpoint
req_body = {
"client_id": app.config["CLIENT_ID"],
"client_secret": app.config["CLIENT_SECRET"],
"grant_type": "authorization_code",
"code": request.args["code"],
}
response = requests.post("https://usetrmnl.com/oauth/token", data=req_body)
try:
with database.atomic():
pi = PluginInstall.create(
access_token=response.json()["access_token"],
)
except IntegrityError:
pass # already installed, redirect
return redirect(request.args["installation_callback_url"])
@app.route("/install", methods=["POST"])
def install_webhook():
# plugin installed webhook
access_token = request.headers["Authorization"].split(" ")[1]
data = request.get_json()
# get pi
pi = PluginInstall.get_or_none(PluginInstall.access_token == access_token)
if not pi:
return "", 404
data["current_task"] = "Figuring out life.."
pi.data = json.dumps(data)
pi.plugin_setting_id = data["user"]["plugin_setting_id"]
pi.plugin_uuid = data["user"]["uuid"]
pi.save()
return "OK"
@app.route("/settings/login")
def settings_login():
# management endpoint
token = request.args["jwt"]
public_key = jwk_client.get_signing_key_from_jwt(token).key
try:
decoded = jwt.decode(
token, public_key, algorithms=["RS256"], audience=app.config["CLIENT_ID"]
)
except Exception as e:
print(e)
return "JWT failed to decode", 403
pi = PluginInstall.get_or_none(PluginInstall.plugin_uuid == decoded["sub"])
if not pi:
return "Failed to find plugin settings.", 404
session["pi"] = pi.id
session["expires"] = int(time.time()) + (60 * 30)
return redirect(url_for("settings"))
def logout_and_redirect():
session.clear()
return redirect("https://usetrmnl.com")
@app.route("/settings", methods=["GET", "POST"])
def settings():
# this is not the route we give to TRMNL management URL, but a second route we use internally
# to clear the token and uuid from the browser logs
# we use cookies to handle this "login" feature
if "pi" not in session or "expires" not in session:
return logout_and_redirect()
if session["expires"] < int(time.time()):
return logout_and_redirect()
pi = PluginInstall.get_or_none(session["pi"])
if not pi:
return logout_and_redirect()
data = json.loads(pi.data)["user"]
# Get the current task from the data or set a default value
pi_data = json.loads(pi.data)
current_task = pi_data.get("current_task", "")
# Handle form submission
if request.method == "POST":
current_task = request.form.get("current_task", "")
# Update the data with the new current_task
pi_data["current_task"] = current_task
pi.data = json.dumps(pi_data)
pi.save()
# log the user out and redirect them back to TRMNL
session.clear()
return redirect(
f"https://usetrmnl.com/plugin_settings/{pi.plugin_setting_id}/edit?force_refresh=true"
)
# Render the template with the data
return render_template(
"settings.j2",
first_name=data["first_name"],
plugin_uuid=pi.plugin_uuid,
plugin_setting_id=pi.plugin_setting_id,
current_task=current_task,
)
@app.route("/markup", methods=["POST"])
def markup():
# markup endpoint
access_token = request.headers["Authorization"].split(" ")[1]
pi = PluginInstall.get_or_none(PluginInstall.access_token == access_token)
if not pi:
return "Failed to find plugin settings. ", 404
data = json.loads(pi.data)
current_task = data.get("current_task", "")
ret = {
"markup": render_template(
"markup.j2", current_task=current_task, view_type="full"
)
}
for view_type in [
"half_horizontal",
"half_vertical",
"quadrant",
]:
ret[f"markup_{view_type}"] = render_template(
"markup.j2", current_task=current_task, view_type=view_type
)
return ret
@app.route("/uninstall", methods=["POST"])
def uninstall():
# uninstall endpoint
access_token = request.headers["Authorization"].split(" ")[1]
pi = PluginInstall.get_or_none(PluginInstall.access_token == access_token)
if not pi:
return "", 404
pi.delete_instance()
return "OK"
@app.before_request
def before_request():
g.db = database
g.db.connect()
@app.after_request
def after_request(response):
g.db.close()
return response
with database:
database.create_tables([PluginInstall])
if __name__ == "__main__":
app.run(debug=True, port=8009)