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
|
from __future__ import annotations
from typing import TYPE_CHECKING
from wsgiref.util import shift_path_info
from flask import Flask, request
from lektor_ng.admin.context import LektorApp, LektorInfo
from lektor_ng.admin.modules import api, dash, livereload, serve
from lektor_ng.environment import Environment
if TYPE_CHECKING:
from _typeshed import StrPath
from _typeshed.wsgi import WSGIApplication
def _common_configuration(app: Flask, debug: bool = False) -> None:
app.debug = debug
app.config["PROPAGATE_EXCEPTIONS"] = True
def make_app(
env: Environment,
debug: bool = False,
output_path: StrPath | None = None,
ui_lang: str = "en",
verbosity: int = 0,
extra_flags: dict[str, str] | None = None,
*,
admin_path: str = "/admin",
static_folder: StrPath | None = "static", # testing
) -> LektorApp:
if output_path is None:
output_path = env.project.get_output_path()
lektor_info = LektorInfo(env, output_path, verbosity, extra_flags)
# The top-level app has a route that matches anything
# ("/<path:path>" in the serve blueprint). That means that if
# there is another route whose doesn't match based on request
# method, the serve view will take over and try to serve it. To
# prevent this from happening for the paths under /admin, we
# structure them as a separate flask app.
admin_app = LektorApp(lektor_info)
_common_configuration(admin_app, debug=debug)
admin_app.config["lektor.ui_lang"] = ui_lang
admin_app.register_blueprint(dash.bp, url_prefix="/")
admin_app.register_blueprint(api.bp, url_prefix="/api")
# Serve static files from top-level app
app = LektorApp(lektor_info, static_url_path=f"{admin_path}/static", static_folder=static_folder)
_common_configuration(app, debug=debug)
app.register_blueprint(livereload.bp, url_prefix="/__reload__")
app.register_blueprint(serve.bp)
# Pass requests for /admin/... to the admin app
@app.route(f"{admin_path}/", defaults={"page": ""})
@app.route(f"{admin_path}/<path:page>", methods=["GET", "POST", "PUT"])
def admin_view(page: str) -> WSGIApplication:
environ = request.environ
# Save top-level SCRIPT_NAME (used by dash)
environ["lektor.site_root"] = request.root_path
while environ.get("PATH_INFO", "") != f"/{page}":
assert environ["PATH_INFO"]
shift_path_info(request.environ)
return admin_app.wsgi_app
# Add rule to construct URL to /admin/edit
app.add_url_rule(f"{admin_path}/edit", "url.edit", build_only=True)
return app
WebAdmin = WebUI = make_app
|