Search code examples
flaskgoogle-app-enginestatic-filesapp.yaml

Serve common static files with App Engine and Flask


I'm writing a service with 2 blueprints(auth & business), each of the blueprint is located in its own package, I need to share some images, scripts and styles accross the Blueprints.

When placing the flash.js and favicon.png in the appropriate folder(styles, images or scripts) in the root static folder is not being fetched when deployed to GCP, while running the app locally the files are being routed properly.

What is the appropriate way to share common static files across blueprints when deploying to GAE?

The Blueprints are defined as follow:

auth_bp = Blueprint(
    'auth',
    __name__,
    template_folder='templates',
    static_folder='static',
    static_url_path='/auth/static/'
)
business_bp = Blueprint(
    'business',
    __name__,
    template_folder='templates',
    static_folder='static',
    static_url_path='/business/static/'
)

The package structure:

├── app
│   ├── __init__.py
│   ├── auth
│   │   ├── __init__.py
│   │   ├── blueprint.py
│   │   ├── constants.py
│   │   ├── forms.py
│   │   ├── static
│   │   │   ├── images
│   │   │   │   └── favicon.png
│   │   │   ├── scripts
│   │   │   │   ├── captcha.js
│   │   │   │   ├── login.js
│   │   │   │   └── reset-password.js
│   │   │   └── styles
│   │   │       └── main.css
│   │   ├── templates
│   │   │   ├── base-auth.html
│   │   │   ├── login.html
│   │   │   ├── reset-password.html
│   │   │   └── reset-request.html
│   │   └── utilities.py
│   ├── business
│   │   ├── __init__.py
│   │   ├── blueprint.py
│   │   ├── static
│   │   └── templates
│   ├── classes
│   │   ├── __init__.py
│   │   └── system_user.py
│   ├── config.py
│   ├── static
│   │   ├── images
│   │   ├── scripts
│   │   │   └── flash.js
│   │   └── styles
│   ├── templates
│   │   └── _macros.html
│   └── utilities
│       ├── __init__.py
│       └── utilities.py
├── app.yaml
├── deployer.sh
├── main.py
└── requirements.txt

app.yaml:

runtime: python311
default_expiration: "2h"

handlers:
  - url: /static
    static_dir: static
  - url: /.*
    script: auto
    secure: always

This would be a sample on how I want to use the shared files:

<head>
  ...
  <link rel="apple-touch-icon" href="{{ url_for('static', filename = 'images/favicon.png') }}" />
  <link rel="icon" sizes="192x192" href="{{ url_for('static', filename = 'images/favicon.png') }}" type="image/png" />
  <script src="{{ url_for('auth.static', filename = 'scripts/captcha.js') }}" defer></script>
  <script src="{{ url_for('static', filename = 'scripts/flash.js') }}" defer></script>
  ...
</head>

The favicon.png I want to use it accross the application, along with the flash.js while the captcha.js, I want it only on the auth Blueprint.

Locally, this setup is working fine, is when I deploy to GAE that doesn't work anymore as expected(the shared static files return 404 and the endpoint is not created), so my guess is that my app.yaml needs fine tuning, but can't find a resource to lead my in the right direction.

UPDATE I

I removed the static declaration from app.yaml and at least the script flash.js worked as expected when deploying.

However I tested the auth blueprint and current_app static_folder and static_url_path when deployed with and without the static declaration on the app.yaml file and in both cases is the same, but the resources are missing when the declaration is on de yaml file.

In both cases only the auth/static is available on the developer console under sources in Chrome.

app.yaml with the static declaration Static handler in yaml

app.yaml without the static declaration No Static handler declaration in yaml

To move on, removed the files and hosted them on a Storage Bucket.

UPDATE II

When checking the source in the rendered webpage the path created by url_for seemed correct static/scripts/flash.js for the scripts and static/images/favicon.png for the images, but the favicon was nowhere to be found(404) while the script was available only when I removed the static declaration from the app.yaml.


Solution

  • I created a dummy project to try to reproduce your issue.

    1. Below is the project folder structure (project is called common_static_files and I have 2 sub apps called app_1 and app_2

      enter image description here

    2. The blueprint definition in app_1

      app_1_handlers = Blueprint("app_1_handlers", __name__, 
          template_folder="templates",
          static_folder='static',
          static_url_path='/app_1/static/')
      
    3. The contents of app.yaml file

      # This handles static files under the app_2 folder
      - url: /app_2/static
        static_dir: common_static_files/app_2/static
      
      # This handles static files under the app_1 folder
      - url: /app_1/static
        static_dir: common_static_files/app_1/static
      
      # This handles static files in the common static folder (in the root of the project)
      - url: /static
        static_dir: common_static_files/static/
      
    4. The contents of app_1.htm under the templates folder in app_1 folder

      Notice how the package name is used for the static handler for app_1

      
      <head>
          <link href="{{ url_for('app_1_handlers.static', filename = 'css/app_1.css') }}" rel="stylesheet">
      </head>
      
      <body >
      
          <div style="font-size: 3em; margin-top: 20px;">{{message}} </div>
          <div style="margin-top:20px;">
              <img src="{{ url_for('static', filename = 'images/question.png') }}" />
          </div>
      
      </body>
      
      
      
    5. The output when the page is loaded

      a) The css file with a black background and white text color is under app_1 > static > css > app_1.css

      b) The image (screenshot of the stackoverflow question) is in the common static folder

      enter image description here