如何用烧瓶服务静态网站
How to serve static site with flask
我使用 MKdocs 创建一个非常敏感的文档,我希望它可以安全地在线访问。
我的项目是:
- [V] 在服务器上创建加密卷(我使用了 veracrypt、cli 和 python 兼容性)
- [V]制作一个简单的页面来索要解密的密钥
- [V]解密卷
- [X] 在烧瓶上提供站点
- [V] 检测会话关闭并再次加密卷
(这样服务器上就没有持久化的明文数据了)
文档仅在本地编辑、加密并同步到服务器。
现在,我在 flask 步骤的服务站点上被阻止了,因为 MKdocs 站点的结构如下:
├── 404.html
├── documentation
│ ├── Folder 1
│ │ ├── Sub folder 1
│ │ │ └── index.html
│ │ └── Sub Folder 2
│ │ └── index.html
│ ├── Folder 2
├── css
│ └── ...
├── fonts
│ └── ...
├── img
│ └── ...
├── index.html
├── js
│ └── ...
├── search
│ └── ...
└── ...
我试图将此内容放在 ./static/site/
下,然后用 flask
替换所有本地 href
和 src
链接
{{ url_for('static', filename='site/[original_content]) }}
但据我所知,它无法在像
这样的静态调用上工作
app.send_static_file('site/index.html')
因为没有替换{{ .. }}
的内容(而且我预料到了这个问题,是静态页面)
问题:
有没有一种方法可以使用 flask 为具有这种“复杂”结构的静态站点提供服务?
MORE and not necessary information, spoiler is not part of question!:
就我能接受的关于如何以不同方式做整件事的任何建议而言,现在,我想实现它。
我想到的其他解决方案:
- 与 nginx 一起服务静态网站,在一个空文件夹上,使用以前的机制的 flask 将解密该卷。不知道如何检测nginx会话结束再加密
- 将所有结构压缩在一个大 HTML 文件中。这将是 很棒的 但我确实浪费了太多时间进行搜索和尝试,但是......我的研究没有运气。 (但这是我推荐的解决方案)
我们可以将 MkDocs 呈现的 HTML 页面视为简单的 Flask 模板。所以我们所要做的就是创建一个 Flask 端点,它首先执行 permissions-checking 然后基于 URL,服务于呈现的 MkDocs HTML 文件或相关的静态文件。
让我们称我们的新端点为 bridge
。首先,将 MkDocs site
目录中的所有内容放到 Flask 的 templates/bridge
目录中。我的示例 MkDocs 树如下所示:
$ tree templates/bridge
templates/bridge
├── 404.html
├── css
│ ├── base.css
│ ├── bootstrap.min.css
│ └── font-awesome.min.css
├── dir1
│ ├── sub1
│ │ └── index.html
│ └── sub2
│ └── index.html
├── dir2
│ ├── sub1
│ │ └── index.html
│ └── sub2
│ └── index.html
├── fonts
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── img
│ ├── favicon.ico
│ └── grid.png
├── index.html
├── js
│ ├── base.js
│ ├── bootstrap.min.js
│ └── jquery-1.10.2.min.js
├── search
│ ├── lunr.js
│ ├── main.js
│ ├── search_index.json
│ └── worker.js
├── sitemap.xml
└── sitemap.xml.gz
我们的新 Flask bridge
端点:
from flask import Flask, render_template, send_from_directory
app = Flask(__name__)
@app.route('/bridge/')
@app.route('/bridge/<path:p1>/')
@app.route('/bridge/<path:p1>/<path:p2>/')
@app.route('/bridge/<path:p1>/<path:p2>/<path:p3>/')
def bridge(p1=None, p2=None, p3=None):
# Permissions checking...
# Serve MkDocs's static files requested from CSS files
if p1 == 'css' and p2 in ('img', 'fonts'):
# CSS fix, e.g. /bridge/css/img/example.png -> /bridge/img/example.png
return send_from_directory(f'templates/bridge/{p2}/', p3)
# Serve MkDocs's static files
if p1 in ('css', 'js', 'fonts', 'search'):
return send_from_directory(f'templates/bridge/{p1}/', p2)
# Serve rendered MkDocs HTML files
if p3 != None:
template = f'bridge/{p1}/{p2}/{p3}/index.html'
elif p2 != None:
template = f'bridge/{p1}/{p2}/index.html'
elif p1 != None:
template = f'bridge/{p1}/index.html'
else:
template = 'bridge/index.html'
return render_template(template)
如您所见,我们创建了几个路径定义。由于 MkDocs 到处都使用相对路径,MkDocs 中的 URL 路径 dir1/sub1/
将变为 https://yoursite.com/bridge/dir1/sub1/
因此我们可以使用此 URL 路由方案和 URL 部分捕获它们将进入 p1
、p2
、p3
路径变量,我们将使用这些变量来提供相应的内容。
有两种类型的内容:静态文件(例如 CSS 文件或图像)和 HTML 内容文件。静态文件将位于 css
、js
、images
、fonts
等目录中,因此当 p1
等于其中之一时,我们使用 Flask 的 send_from_directory
函数来为他们服务。从 CSS 文件引用的静态文件需要进行特定处理,因为 MkDocs 也在那里使用相对路径。
而对于呈现的index.html
文件,我们只需要根据路径和return选择的index.html
文件确定嵌套级别作为普通Flask模板。由于 MkDocs 使用相对 URLs 我们不需要修改呈现的 HTML 文件中的任何内容,每个 URL 都会收到 /bridge/
前缀,因此我们可以为它们提供服务我们的 bridge
端点。
您应该在 bridge
的开头进行权限检查(或作为装饰器,具体取决于您的解决方案)。如果你有更深的嵌套内容,你可能需要添加 p4
and/or p5
路径变量,但它是我示例的直接扩展。
注意:404 错误页面也将由 Flask 提供。
我对你的代码有很多问题,我在解密后重定向到 /bridge/index.html
,从那里点击的任何内容都会以 /bridge/index.html/resource/p1/p2/etc..
开头
之后我不得不重新考虑 p1、p2 和其他人的位置,我发现完全基于 Dauros 解决方案的解决方案,我真正喜欢的是:
@app.route('/<path:p1>/')
@app.route('/<path:p1>/<path:p2>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/<path:p4>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/<path:p4>/<path:p5>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/<path:p4>/<path:p5>/<path:p6>/')
def bridge(p1=None, p2=None, p3=None, p4=None, p5=None, p6=None):
# Permissions checking...
# I'm planning on using basic auth from nginx to even try to show a page
resource = '/'.join([p for p in (p1,p2,p3,p4,p5,p6) if p])
the_file = resource.split('/')[-1]
the_path = '/'.join(resource.split('/')[:-1])
if p1 in ('css', 'fonts', 'img', 'js', 'search'):
return send_from_directory(f'templates/bridge/{the_path}/', the_file)
else:
template = f'bridge/{resource}/index.html'
return render_template(template)
@app.route('/', methods=HTTP_METHODS)
def home():
method = request.method
if method == "GET":
# TO BE CHANGED, checking if volume is already decrypted.
if os.path.exists(f"{BASE_FODLER}/locked"):
# decrypt page
resp = make_response(render_template('decrypt.html'))
return resp
else:
template = 'bridge/index.html'
return render_template(template)
elif method == "POST":
if 'password' in request.form:
decrypt_key = request.form['password']
# decryption omitted
template = 'bridge/index.html'
return render_template(template)
else:
return not_allowed_ans()
else:
return not_allowed_ans()
return SendOk()
如您所见,从底部开始,template = 'bridge/index.html'
然后 return render_template(template)
将网桥站点置于根目录 mystime.com/
这意味着所有路由都必须是根目录。
任何“link”将是:mysite.com/folder_1/sub_folder_1/index.html
之后我让我在未来添加 pX 变得简单(使用动态 PX 的单一路由会很好,但是,这对于静态文档来说已经足够了!)
我使用 MKdocs 创建一个非常敏感的文档,我希望它可以安全地在线访问。
我的项目是:
- [V] 在服务器上创建加密卷(我使用了 veracrypt、cli 和 python 兼容性)
- [V]制作一个简单的页面来索要解密的密钥
- [V]解密卷
- [X] 在烧瓶上提供站点
- [V] 检测会话关闭并再次加密卷
(这样服务器上就没有持久化的明文数据了)
文档仅在本地编辑、加密并同步到服务器。
现在,我在 flask 步骤的服务站点上被阻止了,因为 MKdocs 站点的结构如下:
├── 404.html
├── documentation
│ ├── Folder 1
│ │ ├── Sub folder 1
│ │ │ └── index.html
│ │ └── Sub Folder 2
│ │ └── index.html
│ ├── Folder 2
├── css
│ └── ...
├── fonts
│ └── ...
├── img
│ └── ...
├── index.html
├── js
│ └── ...
├── search
│ └── ...
└── ...
我试图将此内容放在 ./static/site/
下,然后用 flask
href
和 src
链接
{{ url_for('static', filename='site/[original_content]) }}
但据我所知,它无法在像
这样的静态调用上工作app.send_static_file('site/index.html')
因为没有替换{{ .. }}
的内容(而且我预料到了这个问题,是静态页面)
问题: 有没有一种方法可以使用 flask 为具有这种“复杂”结构的静态站点提供服务?
MORE and not necessary information, spoiler is not part of question!:
就我能接受的关于如何以不同方式做整件事的任何建议而言,现在,我想实现它。
我想到的其他解决方案:
- 与 nginx 一起服务静态网站,在一个空文件夹上,使用以前的机制的 flask 将解密该卷。不知道如何检测nginx会话结束再加密
- 将所有结构压缩在一个大 HTML 文件中。这将是 很棒的 但我确实浪费了太多时间进行搜索和尝试,但是......我的研究没有运气。 (但这是我推荐的解决方案)
我们可以将 MkDocs 呈现的 HTML 页面视为简单的 Flask 模板。所以我们所要做的就是创建一个 Flask 端点,它首先执行 permissions-checking 然后基于 URL,服务于呈现的 MkDocs HTML 文件或相关的静态文件。
让我们称我们的新端点为 bridge
。首先,将 MkDocs site
目录中的所有内容放到 Flask 的 templates/bridge
目录中。我的示例 MkDocs 树如下所示:
$ tree templates/bridge
templates/bridge
├── 404.html
├── css
│ ├── base.css
│ ├── bootstrap.min.css
│ └── font-awesome.min.css
├── dir1
│ ├── sub1
│ │ └── index.html
│ └── sub2
│ └── index.html
├── dir2
│ ├── sub1
│ │ └── index.html
│ └── sub2
│ └── index.html
├── fonts
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── img
│ ├── favicon.ico
│ └── grid.png
├── index.html
├── js
│ ├── base.js
│ ├── bootstrap.min.js
│ └── jquery-1.10.2.min.js
├── search
│ ├── lunr.js
│ ├── main.js
│ ├── search_index.json
│ └── worker.js
├── sitemap.xml
└── sitemap.xml.gz
我们的新 Flask bridge
端点:
from flask import Flask, render_template, send_from_directory
app = Flask(__name__)
@app.route('/bridge/')
@app.route('/bridge/<path:p1>/')
@app.route('/bridge/<path:p1>/<path:p2>/')
@app.route('/bridge/<path:p1>/<path:p2>/<path:p3>/')
def bridge(p1=None, p2=None, p3=None):
# Permissions checking...
# Serve MkDocs's static files requested from CSS files
if p1 == 'css' and p2 in ('img', 'fonts'):
# CSS fix, e.g. /bridge/css/img/example.png -> /bridge/img/example.png
return send_from_directory(f'templates/bridge/{p2}/', p3)
# Serve MkDocs's static files
if p1 in ('css', 'js', 'fonts', 'search'):
return send_from_directory(f'templates/bridge/{p1}/', p2)
# Serve rendered MkDocs HTML files
if p3 != None:
template = f'bridge/{p1}/{p2}/{p3}/index.html'
elif p2 != None:
template = f'bridge/{p1}/{p2}/index.html'
elif p1 != None:
template = f'bridge/{p1}/index.html'
else:
template = 'bridge/index.html'
return render_template(template)
如您所见,我们创建了几个路径定义。由于 MkDocs 到处都使用相对路径,MkDocs 中的 URL 路径 dir1/sub1/
将变为 https://yoursite.com/bridge/dir1/sub1/
因此我们可以使用此 URL 路由方案和 URL 部分捕获它们将进入 p1
、p2
、p3
路径变量,我们将使用这些变量来提供相应的内容。
有两种类型的内容:静态文件(例如 CSS 文件或图像)和 HTML 内容文件。静态文件将位于 css
、js
、images
、fonts
等目录中,因此当 p1
等于其中之一时,我们使用 Flask 的 send_from_directory
函数来为他们服务。从 CSS 文件引用的静态文件需要进行特定处理,因为 MkDocs 也在那里使用相对路径。
而对于呈现的index.html
文件,我们只需要根据路径和return选择的index.html
文件确定嵌套级别作为普通Flask模板。由于 MkDocs 使用相对 URLs 我们不需要修改呈现的 HTML 文件中的任何内容,每个 URL 都会收到 /bridge/
前缀,因此我们可以为它们提供服务我们的 bridge
端点。
您应该在 bridge
的开头进行权限检查(或作为装饰器,具体取决于您的解决方案)。如果你有更深的嵌套内容,你可能需要添加 p4
and/or p5
路径变量,但它是我示例的直接扩展。
注意:404 错误页面也将由 Flask 提供。
我对你的代码有很多问题,我在解密后重定向到 /bridge/index.html
,从那里点击的任何内容都会以 /bridge/index.html/resource/p1/p2/etc..
之后我不得不重新考虑 p1、p2 和其他人的位置,我发现完全基于 Dauros 解决方案的解决方案,我真正喜欢的是:
@app.route('/<path:p1>/')
@app.route('/<path:p1>/<path:p2>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/<path:p4>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/<path:p4>/<path:p5>/')
@app.route('/<path:p1>/<path:p2>/<path:p3>/<path:p4>/<path:p5>/<path:p6>/')
def bridge(p1=None, p2=None, p3=None, p4=None, p5=None, p6=None):
# Permissions checking...
# I'm planning on using basic auth from nginx to even try to show a page
resource = '/'.join([p for p in (p1,p2,p3,p4,p5,p6) if p])
the_file = resource.split('/')[-1]
the_path = '/'.join(resource.split('/')[:-1])
if p1 in ('css', 'fonts', 'img', 'js', 'search'):
return send_from_directory(f'templates/bridge/{the_path}/', the_file)
else:
template = f'bridge/{resource}/index.html'
return render_template(template)
@app.route('/', methods=HTTP_METHODS)
def home():
method = request.method
if method == "GET":
# TO BE CHANGED, checking if volume is already decrypted.
if os.path.exists(f"{BASE_FODLER}/locked"):
# decrypt page
resp = make_response(render_template('decrypt.html'))
return resp
else:
template = 'bridge/index.html'
return render_template(template)
elif method == "POST":
if 'password' in request.form:
decrypt_key = request.form['password']
# decryption omitted
template = 'bridge/index.html'
return render_template(template)
else:
return not_allowed_ans()
else:
return not_allowed_ans()
return SendOk()
如您所见,从底部开始,template = 'bridge/index.html'
然后 return render_template(template)
将网桥站点置于根目录 mystime.com/
这意味着所有路由都必须是根目录。
任何“link”将是:mysite.com/folder_1/sub_folder_1/index.html
之后我让我在未来添加 pX 变得简单(使用动态 PX 的单一路由会很好,但是,这对于静态文档来说已经足够了!)