diff --git a/appengine/flexible/static_files_spa/README.md b/appengine/flexible/static_files_spa/README.md new file mode 100644 index 00000000000..4e7366909c0 --- /dev/null +++ b/appengine/flexible/static_files_spa/README.md @@ -0,0 +1,11 @@ +# Python / Flask static files sample for Google App Engine Standard and Flexible Environments + +[![Open in Cloud Shell][shell_img]][shell_link] + +[shell_img]: http://gstatic.com/cloudssh/images/open-btn.png +[shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=appengine/flexible/static_files/README.md + +This demonstrates how to use [Flask](http://flask.pocoo.org/) to serve static files in a Single-Page Application scenario +(where all routes should return the same root html page). + +Refer to the [top-level README](../README.md) for instructions on running and deploying. diff --git a/appengine/flexible/static_files_spa/app.standard.yaml b/appengine/flexible/static_files_spa/app.standard.yaml new file mode 100644 index 00000000000..2b8a6993dc3 --- /dev/null +++ b/appengine/flexible/static_files_spa/app.standard.yaml @@ -0,0 +1,16 @@ +# Copyright 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +runtime: python39 +entrypoint: gunicorn -b :$PORT main:app diff --git a/appengine/flexible/static_files_spa/app.yaml b/appengine/flexible/static_files_spa/app.yaml new file mode 100644 index 00000000000..78ba51b3d4f --- /dev/null +++ b/appengine/flexible/static_files_spa/app.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +runtime: python +env: flex +entrypoint: gunicorn -b :$PORT main:app + +runtime_config: + python_version: 3 diff --git a/appengine/flexible/static_files_spa/main.py b/appengine/flexible/static_files_spa/main.py new file mode 100644 index 00000000000..d614dcc80b7 --- /dev/null +++ b/appengine/flexible/static_files_spa/main.py @@ -0,0 +1,51 @@ +# Copyright 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# [START gae_flex_python_static_files_spa] +import logging +import os +from flask import Flask, send_from_directory + +# folder for static content relative to the current module +STATIC_DIR = os.getenv('STATIC_DIR') or 'static' + +app = Flask(__name__) + + +@app.route('/', defaults={'path': ''}) +@app.route('/') +def catch_all(path): + # NOTE: we don't use Flask standard support for static files + # (static_folder option and send_static_file method) + # because they can't distinguish requests for static files (js/css) + # and client routes (like /products) + file_requested = os.path.join(app.root_path, STATIC_DIR, path) + if not os.path.isfile(file_requested): + path = "index.html" + return send_from_directory(STATIC_DIR, path) + + +@app.errorhandler(500) +def server_error(e): + logging.exception('An error occurred during a request.') + return """ + An internal error occurred:
{}
+ See logs for full stacktrace. + """.format(e), 500 + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See entrypoint in app.yaml. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END gae_flex_python_static_files_spa] diff --git a/appengine/flexible/static_files_spa/main_test.py b/appengine/flexible/static_files_spa/main_test.py new file mode 100644 index 00000000000..33fb21f12ac --- /dev/null +++ b/appengine/flexible/static_files_spa/main_test.py @@ -0,0 +1,29 @@ +# Copyright 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import main + + +def test_index(): + main.app.testing = True + client = main.app.test_client() + + r = client.get('/') + assert r.status_code == 200 + + r = client.get('/main.css') + assert r.status_code == 200 + + r = client.get('/not_existing_route') + assert r.status_code == 200 diff --git a/appengine/flexible/static_files_spa/requirements-test.txt b/appengine/flexible/static_files_spa/requirements-test.txt new file mode 100644 index 00000000000..79738af5f26 --- /dev/null +++ b/appengine/flexible/static_files_spa/requirements-test.txt @@ -0,0 +1 @@ +pytest==5.4.3 diff --git a/appengine/flexible/static_files_spa/requirements.txt b/appengine/flexible/static_files_spa/requirements.txt new file mode 100644 index 00000000000..7179b09a0e2 --- /dev/null +++ b/appengine/flexible/static_files_spa/requirements.txt @@ -0,0 +1,2 @@ +Flask==1.1.2 +gunicorn==20.0.4 diff --git a/appengine/flexible/static_files_spa/static/index.html b/appengine/flexible/static_files_spa/static/index.html new file mode 100644 index 00000000000..bae01e1f75b --- /dev/null +++ b/appengine/flexible/static_files_spa/static/index.html @@ -0,0 +1,43 @@ + + + + + + Static Files + + + + + + + +

This is a static file serving example with SPA support.

+

Mind the absence of page reloads on activating pages via the menu

+

+

+ + diff --git a/appengine/flexible/static_files_spa/static/main.css b/appengine/flexible/static_files_spa/static/main.css new file mode 100644 index 00000000000..c1d74633d78 --- /dev/null +++ b/appengine/flexible/static_files_spa/static/main.css @@ -0,0 +1,19 @@ +/* +Copyright 2021 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +body { + font-family: Verdana, Helvetica, sans-serif; + background-color: #CCCCFF; +}