-
Notifications
You must be signed in to change notification settings - Fork 4
/
app.py
335 lines (256 loc) · 8.99 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
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
"""Library registry web application."""
import os
import sys
import urllib.parse
from flask import Flask, Response
from flask_babel import Babel
from app_helpers import (
compressible,
has_library_factory,
require_admin_authentication,
uses_location_factory,
)
from config import Configuration
from controller import LibraryRegistry
from flask_sqlalchemy_session import flask_scoped_session
from log import LogConfiguration
from model import ConfigurationSetting, SessionManager
from util.app_server import (
returns_json_or_response_or_problem_detail,
returns_problem_detail,
)
from util.xray import PalaceXrayUtils
app = Flask(__name__)
babel = Babel(app)
# Create annotators for this app.
has_library = has_library_factory(app)
uses_location = uses_location_factory(app)
testing = "TESTING" in os.environ
db_url = Configuration.database_url(testing)
SessionManager.initialize(db_url)
session_factory = SessionManager.sessionmaker(db_url)
_db = flask_scoped_session(session_factory, app)
log_level = LogConfiguration.initialize(_db, testing=testing)
debug = log_level == "DEBUG"
app.config["DEBUG"] = debug
app.debug = debug
app._db = _db
# Setup AWS XRay for the app
PalaceXrayUtils.configure_app(app)
if os.environ.get("AUTOINITIALIZE") == "False":
pass
# It's the responsibility of the importing code to set app.library_registry
# appropriately.
else:
if getattr(app, "library_registry", None) is None:
app.library_registry = LibraryRegistry(_db)
def initialize(arbiter):
"""To be called by gunicorn on server startup"""
set_secret_key()
def set_secret_key(_db=None):
_db = _db or app._db
app.secret_key = ConfigurationSetting.sitewide_secret(_db, Configuration.SECRET_KEY)
@app.teardown_request
def shutdown_session(exception):
"""Commit or rollback the database session associated with the request."""
if (
hasattr(
app,
"library_registry",
)
and hasattr(app.library_registry, "_db")
and app.library_registry._db
):
if exception:
app.library_registry._db.rollback()
else:
app.library_registry._db.commit()
@app.route("/")
@uses_location
@returns_problem_detail
def nearby(_location):
return app.library_registry.registry_controller.nearby(_location)
@app.route("/qa")
@uses_location
@returns_problem_detail
def nearby_qa(_location):
return app.library_registry.registry_controller.nearby(_location, live=False)
@app.route("/register", methods=["GET", "POST"])
@returns_problem_detail
def register():
return app.library_registry.registry_controller.register()
@app.route("/search")
@uses_location
@returns_problem_detail
def search(_location):
return app.library_registry.registry_controller.search(_location)
@app.route("/qa/search")
@uses_location
@returns_problem_detail
def search_qa(_location):
return app.library_registry.registry_controller.search(_location, live=False)
@app.route("/confirm/<int:resource_id>/<secret>")
@returns_problem_detail
def confirm_resource(resource_id, secret):
return app.library_registry.validation_controller.confirm(resource_id, secret)
@app.route("/libraries")
@compressible
@uses_location
@returns_problem_detail
def libraries_opds(_location=None):
return app.library_registry.registry_controller.libraries_opds(location=_location)
@app.route("/libraries/qa")
@compressible
@uses_location
@returns_problem_detail
def libraries_qa(_location=None):
return app.library_registry.registry_controller.libraries_opds(
location=_location, live=False
)
@app.route("/admin/log_in", methods=["POST"])
@returns_problem_detail
def log_in():
return app.library_registry.registry_controller.log_in()
@app.route("/admin/log_out")
@returns_problem_detail
def log_out():
return app.library_registry.registry_controller.log_out()
@app.route("/admin/libraries")
@require_admin_authentication
@returns_json_or_response_or_problem_detail
def libraries():
return app.library_registry.registry_controller.libraries()
@app.route("/admin/libraries/qa")
@require_admin_authentication
@returns_json_or_response_or_problem_detail
def libraries_qa_admin():
return app.library_registry.registry_controller.libraries(live=False)
@app.route("/admin/libraries/<uuid>")
@require_admin_authentication
@returns_json_or_response_or_problem_detail
def library_details(uuid):
return app.library_registry.registry_controller.library_details(uuid)
@app.route("/admin/libraries/search_details", methods=["POST"])
@require_admin_authentication
@returns_json_or_response_or_problem_detail
def search_details():
return app.library_registry.registry_controller.search_details()
@app.route("/admin/libraries/email", methods=["POST"])
@require_admin_authentication
@returns_json_or_response_or_problem_detail
def validate_email():
return app.library_registry.registry_controller.validate_email()
@app.route("/admin/libraries/registration", methods=["POST"])
@require_admin_authentication
@returns_json_or_response_or_problem_detail
def edit_registration():
return app.library_registry.registry_controller.edit_registration()
@app.route("/admin/libraries/pls_id", methods=["POST"])
@require_admin_authentication
@returns_json_or_response_or_problem_detail
def pls_id():
return app.library_registry.registry_controller.add_or_edit_pls_id()
@app.route("/library/<uuid>")
@has_library
@returns_json_or_response_or_problem_detail
def library():
return app.library_registry.registry_controller.library()
@app.route("/library/<uuid>/eligibility")
@has_library
@returns_problem_detail
def library_eligibility():
return app.library_registry.coverage_controller.eligibility_for_library()
@app.route("/library/<uuid>/focus")
@has_library
@returns_problem_detail
def library_focus():
return app.library_registry.coverage_controller.focus_for_library()
@app.route("/coverage")
@returns_problem_detail
def coverage():
return app.library_registry.coverage_controller.lookup()
@app.route("/version.json")
def application_version():
return app.library_registry.version.version()
# TODO: This route is deprecated and should be removed in a
# future release of the code, it has been left here for
# one release to ease any deployment issues.
@app.route("/heartbeat")
@returns_problem_detail
def hearbeat():
version_info = application_version()
version_info[
"WARNING"
] = "The /heartbeat endpoint is deprecated. Please use /version.json instead."
return version_info
# Adobe Vendor ID implementation
@app.route("/AdobeAuth/SignIn", methods=["POST"])
@returns_problem_detail
def adobe_vendor_id_signin():
if app.library_registry.adobe_vendor_id:
return app.library_registry.adobe_vendor_id.signin_handler()
else:
return Response("", 404)
@app.route("/AdobeAuth/AccountInfo", methods=["POST"])
@returns_problem_detail
def adobe_vendor_id_accountinfo():
if app.library_registry.adobe_vendor_id:
return app.library_registry.adobe_vendor_id.userinfo_handler()
else:
return Response("", 404)
@app.route("/AdobeAuth/Status")
@returns_problem_detail
def adobe_vendor_id_status():
if app.library_registry.adobe_vendor_id:
return app.library_registry.adobe_vendor_id.status_handler()
else:
return Response("", 404)
# The following two routes serve static files only when
# the app is running locally *without* Docker.
# In all other cases, nginx serves these files (see docker/nginx.conf).
@app.route("/static/registry-admin.js")
@returns_problem_detail
def admin_js():
directory = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
"node_modules",
"simplified-registry-admin",
"dist",
)
return app.library_registry.static_files.static_file(directory, "registry-admin.js")
@app.route("/static/registry-admin.css")
@returns_problem_detail
def admin_css():
directory = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
"node_modules",
"simplified-registry-admin",
"dist",
)
return app.library_registry.static_files.static_file(
directory, "registry-admin.css"
)
@app.route("/admin/", strict_slashes=False)
def admin_view():
return app.library_registry.view_controller()
# This path is used only in debug mode to serve frontend assets.
@app.route("/static/<filename>")
def admin_static_file(filename):
return app.library_registry.static_files.static_file(filename=filename)
if __name__ == "__main__":
debug = True
if len(sys.argv) > 1:
url = sys.argv[1]
else:
url = ConfigurationSetting.sitewide(_db, Configuration.BASE_URL).value
url = url or "http://localhost:7000/"
scheme, netloc, path, parameters, query, fragment = urllib.parse.urlparse(url)
if ":" in netloc:
host, port = netloc.split(":")
port = int(port)
else:
host = netloc
port = 80
app.library_registry.log.info("Starting app on %s:%s", host, port)
initialize(None)
app.run(debug=debug, host=host, port=port)