【问题标题】:How to serve static site with flask如何使用烧瓶服务静态站点
【发布时间】:2022-01-21 22:24:58
【问题描述】:

我使用 MKdocs 创建一个非常敏感的文档,​​我希望它可以安全地在线获取。
我的项目是:

  • [V]在服务器上创建加密卷(我使用veracrypt,cli和python兼容)
  • [V]制作一个简单的页面请求解密密钥
  • [V]解密卷
  • [X] 在烧瓶上服务站点
  • [V] 检测会话关闭并再次加密卷

(这样服务器上就没有持久的清除数据了)

文档只能在本地编辑、加密并重新同步到服务器。

现在,我在烧瓶步骤的服务站点上被阻止,因为 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/ 下,然后用烧瓶替换所有本地hrefsrc 链接

{{ url_for('static', filename='site/[original_content]) }}

但据我所知,它不能用于像这样的静态调用

app.send_static_file('site/index.html')

因为没有替换{{ .. }}的内容(而且我预料到了这个问题,它是一个静态页面)。

问题: 有没有办法使用烧瓶为具有这种“复杂”结构的静态网站提供服务?

更多而非必要的信息,剧透不是问题的一部分!:

只要我能接受任何关于如何以不同方式做整个事情的建议,现在,我想实现它。
我想到的其他解决方案:
- 使用 nginx 提供静态网站,在一个空文件夹上,使用先前机制的烧瓶将解密该卷。我不知道如何检测到nginx会话结束再次加密。
- 将所有结构压缩在一个大 HTML 文件中。这将是太棒了,但我确实浪费了太多时间搜索和尝试,但是......我的研究没有运气。 (但这是我推荐的解决方案)

【问题讨论】:

    标签: python flask static-site mkdocs


    【解决方案1】:

    我们可以将 MkDocs 的渲染 HTML 页面视为简单的 Flask 模板。所以我们所要做的就是创建一个 Flask 端点,它首先进行权限检查,然后基于 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 部分将落在 p1p2p3 路径变量中我们将使用它来提供相应的内容。

    有两种类型的内容:静态文件(例如 CSS 文件或图像)和 HTML 内容文件。静态文件将位于cssjsimagesfonts 等目录中,因此当p1 等于其中之一时,我们使用 Flask 的send_from_directory 函数为它们提供服务。从 CSS 文件引用的静态文件需要进行特定处理,因为 MkDocs 在那里也使用相对路径。

    对于渲染的index.html文件,我们只需要根据路径确定嵌套级别,并将选定的index.html文件作为普通的Flask模板返回。由于 MkDocs 使用相对 URL,我们不必修改呈现的 HTML 文件中的任何内容,每个 URL 都会收到 /bridge/ 前缀,因此我们可以使用 bridge 端点为它们提供服务。

    您应该在bridge 的开头进行权限检查(或作为装饰者,取决于您的解决方案)。如果您的内容嵌套更深,您可能需要添加 p4 和/或 p5 路径变量,但这是对我的示例的直接扩展。

    注意:Flask 也会提供 404 错误页面。

    【讨论】:

      【解决方案2】:

      我的代码有很多问题,解密后我重定向到/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/ 这意味着所有路由都必须是根目录。
      任何“链接”都将是:mysite.com/folder_1/sub_folder_1/index.html
      在那之后,我让我在未来添加 pX 变得很简单(如果有动态 PX 的单一路由会很好,但是,这对于静态文档来说已经足够了!)

      【讨论】:

      • 不客气,很高兴我能帮助您找到解决方案。我只是在计划使用 Django 做类似的事情,所以这是一个用 Flask 进行尝试的好机会。解密后,您应该使用我的解决方案重定向到/bridge/,这是基本路径。 MkDocs 在任何地方都使用相对链接,因此您不必仅附加端点的基本“目录”index.html。您的问题得分为-1,因为有人出于某种原因不喜欢它并单击了否决按钮。它发生了,不要太担心它。我刚刚投了赞成票,所以现在它是中性的 0 分。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-08
      • 1970-01-01
      • 2016-11-29
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多