├── .github └── workflows │ └── main.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── cookiecutter.json ├── discard-dev-files.sh ├── scripts └── generate_cookiecutter_config.py ├── test.sh ├── testing-config.yml └── {{cookiecutter.project_slug}} ├── .env ├── .gitignore ├── .gitlab-ci.yml ├── README.md ├── backend ├── .gitignore ├── Dockerfile └── app │ ├── app │ ├── __init__.py │ ├── cors.py │ └── main.py │ ├── backend-live.sh │ └── uwsgi.ini ├── cookiecutter-config-file.yml ├── docker-compose.deploy.build.yml ├── docker-compose.deploy.command.yml ├── docker-compose.deploy.images.yml ├── docker-compose.deploy.labels.yml ├── docker-compose.deploy.networks.yml ├── docker-compose.deploy.volumes-placement.yml ├── docker-compose.dev.build.yml ├── docker-compose.dev.command.yml ├── docker-compose.dev.labels.yml ├── docker-compose.dev.ports.yml ├── docker-compose.dev.volumes.yml ├── docker-compose.shared.admin.yml ├── docker-compose.shared.env.yml ├── frontend ├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── babel.config.js ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.vue │ ├── assets │ └── logo.png │ ├── components │ └── HelloWorld.vue │ ├── config.js │ └── main.js └── scripts ├── build-push.sh ├── build.sh └── deploy.sh /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: "0 0 * * *" 4 | 5 | jobs: 6 | issue-manager: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: tiangolo/issue-manager@master 10 | with: 11 | token: ${{ secrets.GITHUB_TOKEN }} 12 | config: > 13 | { 14 | "answered": { 15 | "users": ["tiangolo"], 16 | "delay": 864000, 17 | "message": "Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | testing-project 3 | docker-stack.yml 4 | .mypy_cache 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: python 4 | 5 | install: 6 | - pip install cookiecutter 7 | 8 | services: 9 | - docker 10 | 11 | script: 12 | - bash ./test.sh 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sebastián Ramírez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 🚨 DEPRECATION WARNING 🚨 2 | 3 | I'm currently not actively using this generator for any project. 4 | 5 | You might still find some internal pieces of code useful for your own use cases, but I won't be able to fix bugs and add features. 6 | 7 | If you are starting a new project from scratch, check the alternatives at the [FastAPI docs: Project Generation](https://fastapi.tiangolo.com/project-generation/). 8 | 9 | You are still free to use this project if you want to, you might still find some internal pieces of code useful for your own use case. And if you already have a project generated with it that's fine as well (and you probably already updated it to suit your needs). 10 | 11 | # Flask Frontend Docker - project generator 12 | 13 | [](https://travis-ci.org/tiangolo/flask-frontend-docker) 14 | 15 | Generate stack with a a Flask backend and a modern (Vue.js, React, Angular) frontend. 16 | 17 | Each in its own container, with routes handled by a Traefik proxy. 18 | 19 | With automatic HTTPS certificate generation using Let's Encrypt. 20 | 21 | 22 | ## Features 23 | 24 | * Full Docker integration (Docker based) 25 | * Docker Swarm Mode deployment 26 | * Docker Compose integration and optimization for local development 27 | * Production ready Python web server using Nginx and uWSGI 28 | * Python Flask backend: 29 | * CORS already configured, to be used by the frontend. 30 | * Vue frontend 31 | * Easily updated to be Angular or React. 32 | * Docker server based on Nginx (configured to play nicely with Vue-router) 33 | * Docker multi-stage building, so you don't need to save or commit compiled code 34 | * Easily enable frontend tests at build time 35 | * Load balancing between frontend and backend with Traefik: 36 | * So you can have both backend and frontend under the same domain, separated by path, but served by the two different containers 37 | * Automatic (free) HTTPS certificate generation with Let's Encrypt and Traefik 38 | 39 | 40 | ## How to use it 41 | 42 | Go to the directoy where you want to create your project and run: 43 | 44 | ```bash 45 | pip install cookiecutter 46 | cookiecutter https://github.com/tiangolo/flask-frontend-docker 47 | ``` 48 | 49 | ### Input variables 50 | 51 | The generator (cookiecutter) will ask you for some data, you might want to have at hand before generating the project. 52 | 53 | To see the variables asked, check the [`cookiecutter.json` file](./cookiecutter.json). 54 | 55 | ## How to deploy 56 | 57 | This stack can be adjusted and used with several deployment options that are compatible with Docker Compose, but it is designed to be used in a cluster controlled with pure Docker in Swarm Mode with a Traefik main load balancer proxy handling automatic HTTPS certificates, using the ideas from DockerSwarm.rocks. 58 | 59 | Please refer to DockerSwarm.rocks to see how to deploy such a cluster in 20 minutes. 60 | 61 | ## More details 62 | 63 | After using this generator, your new project (the directory created) will contain an extensive `README.md` with instructions for development, deployment, etc. You can pre-read [the project `README.md` template here too](./{{cookiecutter.project_slug}}/README.md). 64 | 65 | ## Alternatives 66 | 67 | This is a very simple project generator, with just the minimum to have: 68 | 69 | * Flask in one Docker container. 70 | * A modern frontend in a separate Docker container: 71 | * With an example minimal Vue application, that can easily be changed to React, Angular, etc. 72 | * Both backend and frontend being served using a single proxy/load-balancer with Traefik: 73 | * Generating automatic HTTPS certificates with Let's Encrypt. 74 | 75 | This doesn't include many useful tools for building APIs, database integrations, more sophisticate frontend features, etc. 76 | 77 | It is just to show the minimum configurations to achieve that. 78 | 79 | If you are building a backend that would be used by a modern frontend (like in this minimal project generator) you might benefit from other additional tools made for APIs. 80 | 81 | This project generator has several bigger (more complete) sibling project generators that you might want to check and use. 82 | 83 | ### FastAPI 84 | 85 | If you are building a backend to be used as an API and a modern frontend that communicates with it (as in this project), you might benefit from using [**FastAPI**](https://github.com/tiangolo/fastapi) instead of Flask, and a corresponding project generator, including a NoSQL database, security, authentication, asynchronous jobs, email notifications, etc: 86 | 87 | https://github.com/tiangolo/full-stack-fastapi-couchbase 88 | 89 | 90 | ### Flask 91 | 92 | If for some reason you can't (or don't want to) use FastAPI and want to keep using Flask, there are also bigger sibling projects: 93 | 94 | https://github.com/tiangolo/full-stack 95 | 96 | https://github.com/tiangolo/full-stack-flask-couchbase 97 | 98 | https://github.com/tiangolo/full-stack-flask-couchdb 99 | 100 | 101 | ## License 102 | 103 | This project is licensed under the terms of the MIT license. 104 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "Base Project", 3 | "project_slug": "{{ cookiecutter.project_name|lower|replace(' ', '-') }}", 4 | "domain_main": "{{cookiecutter.project_slug}}.com", 5 | 6 | "backend_cors_origins": "http://localhost, http://localhost:4200, http://localhost:3000, http://localhost:8080, http://dev.{{cookiecutter.domain_main}}, https://{{cookiecutter.domain_main}}, http://local.dockertoolbox.tiangolo.com, http://localhost.tiangolo.com", 7 | 8 | "docker_swarm_stack_name_main": "{{cookiecutter.domain_main|replace('.', '-')}}", 9 | 10 | "traefik_constraint_tag": "{{cookiecutter.domain_main}}", 11 | "traefik_public_network": "traefik-public", 12 | "traefik_public_constraint_tag": "traefik-public", 13 | 14 | "docker_image_prefix": "", 15 | 16 | "docker_image_backend": "{{cookiecutter.docker_image_prefix}}backend", 17 | "docker_image_frontend": "{{cookiecutter.docker_image_prefix}}frontend", 18 | 19 | "_copy_without_render": [ 20 | "frontend/src/**/*.html", 21 | "frontend/src/**/*.vue", 22 | "frontend/node_modules/*" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /discard-dev-files.sh: -------------------------------------------------------------------------------- 1 | rm -rf \{\{cookiecutter.project_slug\}\}/.git 2 | rm -rf \{\{cookiecutter.project_slug\}\}/frontend/node_modules 3 | rm -rf \{\{cookiecutter.project_slug\}\}/frontend/dist 4 | git checkout \{\{cookiecutter.project_slug\}\}/README.md 5 | git checkout \{\{cookiecutter.project_slug\}\}/.gitlab-ci.yml 6 | git checkout \{\{cookiecutter.project_slug\}\}/cookiecutter-config-file.yml 7 | git checkout \{\{cookiecutter.project_slug\}\}/docker-compose.deploy.networks.yml 8 | git checkout \{\{cookiecutter.project_slug\}\}/.env 9 | git checkout \{\{cookiecutter.project_slug\}\}/frontend/.env 10 | -------------------------------------------------------------------------------- /scripts/generate_cookiecutter_config.py: -------------------------------------------------------------------------------- 1 | import json 2 | from collections import OrderedDict 3 | import oyaml as yaml 4 | from pathlib import Path 5 | cookie_path = Path('./cookiecutter.json') 6 | out_path = Path('./{{cookiecutter.project_slug}}/cookiecutter-config-file.yml') 7 | 8 | with open(cookie_path) as f: 9 | cookie_config = json.load(f) 10 | config_out = OrderedDict() 11 | 12 | for key, value in cookie_config.items(): 13 | if key.startswith('_'): 14 | config_out[key] = value 15 | else: 16 | config_out[key] = '{{ cookiecutter.' + key + ' }}' 17 | config_out['_template'] = './' 18 | 19 | with open(out_path, 'w') as out_f: 20 | out_f.write(yaml.dump({'default_context': config_out}, line_break=None, width=200)) 21 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # Exit in case of error 4 | set -e 5 | 6 | rm -rf ./testing-project 7 | 8 | cookiecutter --config-file ./testing-config.yml --no-input -f ./ 9 | 10 | cd ./testing-project 11 | 12 | bash ./scripts/build.sh 13 | 14 | cd ../ 15 | -------------------------------------------------------------------------------- /testing-config.yml: -------------------------------------------------------------------------------- 1 | default_context: 2 | "project_name": "Testing Project" 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PATH_SEPARATOR=: 2 | COMPOSE_FILE=docker-compose.shared.admin.yml:docker-compose.dev.build.yml:docker-compose.dev.command.yml:docker-compose.dev.labels.yml:docker-compose.dev.ports.yml:docker-compose.dev.volumes.yml 3 | 4 | DOMAIN=localhost 5 | # DOMAIN=local.dockertoolbox.tiangolo.com 6 | # DOMAIN=localhost.tiangolo.com 7 | # DOMAIN=dev.{{cookiecutter.domain_main}} 8 | 9 | TRAEFIK_TAG={{cookiecutter.traefik_constraint_tag}} 10 | TRAEFIK_PUBLIC_NETWORK={{cookiecutter.traefik_public_network}} 11 | TRAEFIK_PUBLIC_TAG={{cookiecutter.traefik_public_constraint_tag}} 12 | 13 | DOCKER_IMAGE_BACKEND={{cookiecutter.docker_image_backend}} 14 | DOCKER_IMAGE_FRONTEND={{cookiecutter.docker_image_frontend}} 15 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .mypy_cache 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: tiangolo/docker-with-compose 2 | 3 | before_script: 4 | - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY 5 | 6 | stages: 7 | - build 8 | - deploy 9 | 10 | build-prod: 11 | stage: build 12 | script: 13 | - TAG=prod FRONTEND_ENV=production sh ./scripts/build-push.sh 14 | only: 15 | - master 16 | tags: 17 | - build 18 | - test 19 | 20 | deploy-prod: 21 | stage: deploy 22 | script: 23 | - > 24 | DOMAIN={{cookiecutter.domain_main}} 25 | TRAEFIK_TAG={{cookiecutter.traefik_constraint_tag}} 26 | STACK_NAME={{cookiecutter.docker_swarm_stack_name_main}} 27 | TAG=prod 28 | sh ./scripts/deploy.sh 29 | environment: 30 | name: production 31 | url: https://{{cookiecutter.domain_main}} 32 | only: 33 | - master 34 | tags: 35 | - swarm 36 | - prod 37 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/README.md: -------------------------------------------------------------------------------- 1 | # {{cookiecutter.project_name}} 2 | 3 | ## Backend Requirements 4 | 5 | * Docker 6 | * Docker Compose 7 | 8 | ## Frontend Requirements 9 | 10 | * Node.js (with `npm`) 11 | 12 | ## Backend local development 13 | 14 | * Start the stack with Docker Compose: 15 | 16 | ```bash 17 | docker-compose up -d 18 | ``` 19 | 20 | * Now you can open your browser and interact with these URLs: 21 | 22 | Frontend, built with Docker, with routes handled based on the path: http://localhost 23 | 24 | Backend, URLs prefixed with: http://localhost/api/ 25 | 26 | Traefik UI, to see how the routes are being handled by the proxy: http://localhost:8090 27 | 28 | To check the logs, run: 29 | 30 | ```bash 31 | docker-compose logs 32 | ``` 33 | 34 | To check the logs of a specific service, add the name of the service, e.g.: 35 | 36 | ```bash 37 | docker-compose logs backend 38 | ``` 39 | 40 | If your Docker is not running in `localhost` (the URLs above wouldn't work) check the sections below on **Development with Docker Toolbox** and **Development with a custom IP**. 41 | 42 | ## Backend local development, additional details 43 | 44 | ### Docker Compose Override 45 | 46 | During development, you can change Docker Compose settings that will only affect the local development environment, in the files `docker-compose.dev.*.yml`. 47 | 48 | The changes to those files only affect the local development environment, not the production environment. So, you can add "temporal" changes that help the development workflow. 49 | 50 | For example, the directory with the backend code is mounted as a Docker "host volume" (in the file `docker-compose.dev.volumes.yml`), mapping the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast. 51 | 52 | There is also a commented out `command` override (in the file `docker-compose.dev.command.yml`), if you want to enable it, uncomment it. It makes the backend container run a process that does "nothing", but keeps the process running. That allows you to get inside your living container and run commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detectes changes. 53 | 54 | To get inside the container with a `bash` session you can start the stack with: 55 | 56 | ```bash 57 | docker-compose up -d 58 | ``` 59 | 60 | and then `exec` inside the running container: 61 | 62 | ```bash 63 | docker-compose exec backend bash 64 | ``` 65 | 66 | You should see an output like: 67 | 68 | ``` 69 | root@7f2607af31c3:/app# 70 | ``` 71 | 72 | that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory. 73 | 74 | There is also a script `backend-live.sh` to run the debug live reloading server. You can run that script from inside the container with: 75 | 76 | ```bash 77 | bash ./backend-live.sh 78 | ``` 79 | 80 | ...it will look like: 81 | 82 | ```bash 83 | root@7f2607af31c3:/app# bash ./backend-live.sh 84 | ``` 85 | 86 | and then hit enter. That runs the debugging server that auto reloads when it detects code changes. 87 | 88 | Nevertheless, if it doesn't detect a change but a syntax error, it will just stop with an error. But as the container is still alive and you are in a Bash session, you can quickly restart it after fixing the error, running the same command ("up arrow" and "Enter"). 89 | 90 | ...this previous detail is what makes it useful to have the container alive doing nothing and then, in a Bash session, make it run the debugging server. 91 | 92 | ### Development with Docker Toolbox 93 | 94 | If you are using **Docker Toolbox** in Windows or macOS instead of **Docker for Windows** or **Docker for Mac**, Docker will be running in a VirtualBox Virtual Machine, and it will have a local IP different than `127.0.0.1`, which is the IP address for `localhost` in your machine. 95 | 96 | The address of your Docker Toolbox virtual machine would probably be `192.168.99.100` (that is the default). 97 | 98 | As this is a common case, the domain `local.dockertoolbox.tiangolo.com` points to that (private) IP, just to help with development (actually `dockertoolbox.tiangolo.com` and all its subdomains point to that IP). That way, you can start the stack in Docker Toolbox, and use that domain for development. You will be able to open that URL in Chrome and it will communicate with your local Docker Toolbox directly as if it was a cloud server, including CORS (Cross Origin Resource Sharing). 99 | 100 | If you used the default CORS enabled domains while generating the project, `local.dockertoolbox.tiangolo.com` was configured to be allowed. If you didn't, you will need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file. 101 | 102 | To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `local.dockertoolbox.tiangolo.com`. 103 | 104 | After performing those steps you should be able to open: http://local.dockertoolbox.tiangolo.com and it will be server by your stack in your Docker Toolbox virtual machine. 105 | 106 | Check all the corresponding available URLs in the section at the end. 107 | 108 | ### Develpment in `localhost` with a custom domain 109 | 110 | You might want to use something different than `localhost` as the domain. For example, if you are having problems with cookies that need a subdomain, and Chrome is not allowing you to use `localhost`. 111 | 112 | In that case, you have two options: you could use the instructions to modify your system `hosts` file with the instructions below in **Development with a custom IP** or you can just use `localhost.tiangolo.com`, it is set up to point to `localhost` (to the IP `127.0.0.1`) and all its subdomains too. And as it is an actual domain, the browsers will store the cookies you set during development, etc. 113 | 114 | If you used the default CORS enabled domains while generating the project, `localhost.tiangolo.com` was configured to be allowed. If you didn't, you will need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file. 115 | 116 | To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `localhost.tiangolo.com`. 117 | 118 | After performing those steps you should be able to open: http://localhost.tiangolo.com and it will be server by your stack in `localhost`. 119 | 120 | Check all the corresponding available URLs in the section at the end. 121 | 122 | 123 | ### Development with a custom IP 124 | 125 | If you are running Docker in an IP address different than `127.0.0.1` (`localhost`) and `192.168.99.100` (the default of Docker Toolbox), you will need to perform some additional steps. That will be the case if you are running a custom Virtual Machine, a secondary Docker Toolbox or your Docker is located in a different machine in your network. 126 | 127 | In that case, you will need to use a fake local domain (`dev.{{cookiecutter.domain_main}}`) and make your computer think that the domain is is served by the custom IP (e.g. `192.168.99.150`). 128 | 129 | If you used the default CORS enabled domains, `dev.{{cookiecutter.domain_main}}` was configured to be allowed. If you want a custom one, you need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file. 130 | 131 | * Open your `hosts` file with administrative privileges using a text editor: 132 | * **Note for Windows**: If you are in Windows, open the main Windows menu, search for "notepad", right click on it, and select the option "open as Administrator" or similar. Then click the "File" menu, "Open file", go to the directory `c:\Windows\System32\Drivers\etc\`, select the option to show "All files" instead of only "Text (.txt) files", and open the `hosts` file. 133 | * **Note for Mac and Linux**: Your `hosts` file is probably located at `/etc/hosts`, you can edit it in a terminal running `sudo nano /etc/hosts`. 134 | 135 | * Additional to the contents it might have, add a new line with the custom IP (e.g. `192.168.99.150`) a space character, and your fake local domain: `dev.{{cookiecutter.domain_main}}`. 136 | 137 | The new line might look like: 138 | 139 | ``` 140 | 192.168.99.100 dev.{{cookiecutter.domain_main}} 141 | ``` 142 | 143 | * Save the file. 144 | * **Note for Windows**: Make sure you save the file as "All files", without an extension of `.txt`. By default, Windows tries to add the extension. Make sure the file is saved as is, without extension. 145 | 146 | ...that will make your computer think that the fake local domain is served by that custom IP, and when you open that URL in your browser, it will talk directly to your locally running server when it is asked to go to `dev.{{cookiecutter.domain_main}}` and think that it is a remote server while it is actually running in your computer. 147 | 148 | To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `dev.{{cookiecutter.domain_main}}`. 149 | 150 | After performing those steps you should be able to open: http://dev.{{cookiecutter.domain_main}} and it will be server by your stack in `localhost`. 151 | 152 | Check all the corresponding available URLs in the section at the end. 153 | 154 | ### Change the development "domain" 155 | 156 | If you need to use your local stack with a different domain than `localhost`, you need to make sure the domain you use points to the IP where your stack is set up. See the different ways to achieve that in the sections above (i.e. using Docker Toolbox with `local.dockertoolbox.tiangolo.com`, using `localhost.tiangolo.com` or using `dev.{{cookiecutter.domain_main}}`). 157 | 158 | To simplify your Docker Compose setup, for example, so that the API explorer, Swagger UI, knows where is your API, you should let it know you are using that domain for development. You will need to edit 1 line in 2 files. 159 | 160 | * Open the file located at `./.env`. It would have a line like: 161 | 162 | ``` 163 | DOMAIN=localhost 164 | ``` 165 | 166 | * Change it to the domain you are going to use, e.g.: 167 | 168 | ``` 169 | DOMAIN=localhost.tiangolo.com 170 | ``` 171 | 172 | That variable will be used by some of the local development `docker-compose.dev.*.yml` files, for example, to tell Swagger UI to use that domain for the API. 173 | 174 | * Now open the file located at `./frontend/.env`. It would have a line like: 175 | 176 | ``` 177 | VUE_APP_DOMAIN_DEV=localhost 178 | ``` 179 | 180 | * Change that line to the domain you are going to use, e.g.: 181 | 182 | ``` 183 | VUE_APP_DOMAIN_DEV=localhost.tiangolo.com 184 | ``` 185 | 186 | That variable will make your frontend communicate with that domain when interacting with your backend API, when the other variable `VUE_APP_ENV` is set to `development`. 187 | 188 | After changing the two lines, you can re-start your stack with: 189 | 190 | ```bash 191 | docker-compose up -d 192 | ``` 193 | 194 | and check all the corresponding available URLs in the section at the end. 195 | 196 | ## Frontend development 197 | 198 | * Enter the `frontend` directory, install the NPM packages and start the live server using the `npm` scripts: 199 | 200 | ```bash 201 | cd frontend 202 | npm install 203 | npm run serve 204 | ``` 205 | 206 | Then open your browser at http://localhost:8080 207 | 208 | Notice that this live server is not running inside Docker, it is for local development, and that is the recommended workflow. Once you are happy with your frontend, you can build the frontend Docker image and start it, to test it in a production-like environment. But compiling the image at every change will not be as productive as running the local development server. 209 | 210 | Check the file `package.json` to see other available options. 211 | 212 | If you have Vue CLI installed, you can also run `vue ui` to control, configure, serve and analyse your application using a nice local web user interface. 213 | 214 | If you are only developing the frontend (e.g. other team members are developing the backend) and there is a staging environment already deployed, you can make your local development code use that staging API instead of a full local Docker Compose stack. 215 | 216 | To do that, modify the file `./frontend/.env`, there's a section with: 217 | 218 | ``` 219 | VUE_APP_ENV=development 220 | # VUE_APP_ENV=staging 221 | ``` 222 | 223 | * Switch the comment, to: 224 | 225 | ``` 226 | # VUE_APP_ENV=development 227 | VUE_APP_ENV=staging 228 | ``` 229 | 230 | ## Deployment 231 | 232 | You can deploy the stack to a Docker Swarm mode cluster with a main Traefik proxy, set up using the ideas from DockerSwarm.rocks, to get automatic HTTPS certificates, etc. 233 | 234 | And you can use CI (continuous integration) systems to do it automatically. 235 | 236 | 237 | ### Deploy to a Docker Swarm mode cluster 238 | 239 | There are 3 steps: 240 | 241 | 1. **Build** your app images 242 | 2. Optionally, **push** your custom images to a Docker Registry 243 | 3. **Deploy** your stack 244 | 245 | --- 246 | 247 | Here are the steps in detail: 248 | 249 | 1. **Build your app images** 250 | 251 | * Set these environment variables, prepended to the next command: 252 | * `TAG=prod` 253 | * `FRONTEND_ENV=production` 254 | * Use the provided `script-build.sh` file with those environment variables: 255 | 256 | ```bash 257 | TAG=prod FRONTEND_ENV=production bash ./script-build.sh 258 | ``` 259 | 260 | 2. **Optionally, push your images to a Docker Registry** 261 | 262 | **Note**: if the deployment Docker Swarm mode "cluster" has more than one server, you will have to push the images to a registry or build the images in each server, so that when each of the servers in your cluster tries to start the containers it can get the Docker images for them, pulling them from a Docker Registry or because it has them already built locally. 263 | 264 | If you are using a registry and pushing your images, you can omit running the previous script and instead using this one, in a single shot. 265 | 266 | * Set these environment variables: 267 | * `TAG=prod` 268 | * `FRONTEND_ENV=production` 269 | * Use the provided `script-build-push.sh` file with those environment variables: 270 | 271 | ```bash 272 | TAG=prod FRONTEND_ENV=production bash ./script-build.sh 273 | ``` 274 | 275 | 3. **Deploy your stack** 276 | 277 | * Set these environment variables: 278 | * `DOMAIN={{cookiecutter.domain_main}}` 279 | * `TRAEFIK_TAG={{cookiecutter.traefik_constraint_tag}}` 280 | * `STACK_NAME={{cookiecutter.docker_swarm_stack_name_main}}` 281 | * `TAG=prod` 282 | * Use the provided `script-deploy.sh` file with those environment variables: 283 | 284 | ```bash 285 | DOMAIN={{cookiecutter.domain_main}} \ 286 | TRAEFIK_TAG={{cookiecutter.traefik_constraint_tag}} \ 287 | STACK_NAME={{cookiecutter.docker_swarm_stack_name_main}} \ 288 | TAG=prod \ 289 | bash ./script-deploy.sh 290 | ``` 291 | 292 | --- 293 | 294 | If you change your mind and, for example, want to deploy everything to a different domain, you only have to change the `DOMAIN` environment variable in the previous commands. If you wanted to add a different version / environment of your stack, like "`preproduction`", you would only have to set `TAG=preproduction` in your command and update these other environment variables accordingly. And it would all work, that way you could have different environments and deployments of the same app in the same cluster. 295 | 296 | #### Deployment Technical Details 297 | 298 | For the 3 steps (build, push, deploy) you need a generated `docker-stack.yml`, it is generated using the `docker-compose` command with some of the `docker-compose.*.yml` files. As each of these steps uses different `docker-compose.*.yml` files, the generated `docker-stack.yml` file is slightly different. But it's all generated by the scripts. 299 | 300 | You can do the process by hand based on those same scripts if you wanted. The general structure of the scripts is like this: 301 | 302 | ```bash 303 | # Use the environment variables passed to this script, as TAG and FRONTEND_ENV 304 | # And re-create those variables as environment variables for the next command 305 | TAG=${TAG} \ 306 | # Set the environment variable FRONTEND_ENV to the same value passed to this script with 307 | # a default value of "production" if nothing else was passed 308 | FRONTEND_ENV=${FRONTEND_ENV-production} \ 309 | # The actual comand that does the work: docker-compose 310 | docker-compose \ 311 | # Pass the files that should be used at this stage, the set of files changes in each script / each stage 312 | -f docker-compose.deploy.build.yml \ 313 | -f docker-compose.deploy.images.yml \ 314 | # Use the docker-compose sub command named "config", it just uses the docker-compose.*.yml files passed 315 | # to it and prints their combined contents 316 | # Put those contents in a file "docker-stack.yml", with ">" 317 | config > docker-stack.yml 318 | 319 | # The previous only generated a docker-stack.yml file, but didn't do anything with it 320 | # Now this command uses that same file and does some operation with it, in this case, build it 321 | docker-compose -f docker-stack.yml build 322 | ``` 323 | ## Docker Compose files 324 | 325 | There are several Docker Compose files, each with a specific purpose. 326 | 327 | They are designed to support several "stages", like development, building, testing, and deployment. Also, allowing the deployment to different environments like staging and production (and you can add more environments very easily). 328 | 329 | They are designed to have the minimum repetition of code and configurations, so that if you need to change something, you have to change it in the minimum amount of places. That's why several of the files use environment variables that get auto-expanded. That way, if for example, you want to use a different domain, you can call the `docker-compose` command with a different `DOMAIN` environment variable instead of having to change the domain in several places inside the Docker Compose files. 330 | 331 | Also, if you want to have another deployment environment, say `preprod`, you just have to change environment variables, but you can keep using the same Docker Compose files. 332 | 333 | Because of that, for each "stage" (development, building, testing, deployment) you would use a different set of Docker Compose files. 334 | 335 | But you probably don't have to worry about the different files, for building, testing and deployment, you would probably use a CI system (like GitLab CI) and the different configured files would be already set there. 336 | 337 | And for development, there's a `.env` file that will be automatically used by `docker-compose` locally, with the default configurations already set for local development. Including environment variables. So, for local development you can just run: 338 | 339 | ```bash 340 | docker-compose up -d 341 | ``` 342 | 343 | and it will do the right thing. 344 | 345 | They are also separated by the common tasks and functionalities they solve, and they are named accordinly. So, although there are many Docker Compose files, each one has a name that shows what should be in there, and the contents tend to be small and specific. That makes it easier to modify, or add configurations, as you can go directly to the relevant file. 346 | 347 | The `docker-compose.deploy.*.yml` files are only used at deployment, being it to production or any other environment. They build the images in production mode (not installing debugging packages), set configurations for Docker Swarm mode, etc. 348 | 349 | The `docker-compose.dev.*.yml` files are only used during development. They have overrides and tools for development, as mounting app volumes directly inside the container to iterate fast, map ports directly to your machine, install debugging packages, etc. 350 | 351 | The `docker-compose.shared.*.yml` files are used at several stages and contain stuff shared by several stages: development, testing, deployment. They have things like the databases or the environment variables, that are used by all the main services / containers, during development, testing and deployment. The file for `admin`, that has utils needed for development and production, like the Swagger UI interactive API documentation system. But this file is not used during testing (in CI environments) as this is not needed or used in that stage. 352 | 353 | The purpose of each Docker Compose file is: 354 | 355 | * `docker-compose.deploy.build.yml`: build directories and `Dockerfile`s, for deployment (the building process for development has a little difference). 356 | * `docker-compose.deploy.command.yml`: command overrides for images only during deployment. Initially only for the main Traefik proxy, making it run in a Docker Swarm mode cluster. 357 | * `docker-compose.deploy.images.yml`: image names to be created, with environment variables for the specific tag. 358 | * `docker-compose.deploy.labels.yml`: labels for deployment, the configurations to make the internal Traefik proxy serve some services on specific URLs, some with basic HTTP auth, etc. Also labels used in the internal Traefik proxy container to make it talk to the public Traefik proxy (outside of this stack) and make it send requests for this domain, generate HTTPS certificates, etc. 359 | * `docker-compose.deploy.networks.yml`: networks that have to be used and shared by containers that need to be able to talk to the public Traefik proxy (when a service requires a domain for itself). 360 | * `docker-compose.deploy.volumes-placement.yml`: volume declarations, volumes used by stateful services (as databases) and volume placement constraints, to make those services always run on the node that has their volumes, even after stack updates. 361 | * `docker-compose.dev.build.yml`: build directories and `Dockerfile`s, for local development, sets a built-time argument that then is used in the `Dockerfile`s to install and configure helper tools exclusively for development. 362 | * `docker-compose.dev.command.yml`: command overrides for local development. To tell the internal Traefik proxy to work with a local Docker in the host instead of a Docker Swarm mode cluster. And (commented out but ready to be used) overrides to make the containers run an infinite loop while keeping alive to be able to run the development server manually or do any other interactive work. 363 | * `docker-compose.dev.labels.yml`: local development labels, to be used by the local development Traefik proxy. They have to be declared in a different place than for deployment. 364 | * `docker-compose.dev.ports.yml`: local development port mappings. 365 | * `docker-compose.dev.volumes.yml`: local development mounted volumes, mainly to map the development code directory inside the container, for fast development without needing to re-build the images. 366 | * `docker-compose.shared.admin.yml`: additional services for administration or utilities with their configurations, like PGAdmin and Swagger, that are not needed during testing and use external images (don't need to be built or create images). 367 | 368 | ## URLs 369 | 370 | These are the URLs that will be used and generated by the project. 371 | 372 | ### Production 373 | 374 | Production URLs, from the branch `production`. 375 | 376 | Frontend: https://{{cookiecutter.domain_main}} 377 | 378 | Backend: https://{{cookiecutter.domain_main}}/api/ 379 | 380 | 381 | ### Development 382 | 383 | Development URLs, for local development. 384 | 385 | Frontend: http://localhost 386 | 387 | Backend: http://localhost/api/ 388 | 389 | Traefik UI: http://localhost:8090 390 | 391 | ### Development with Docker Toolbox 392 | 393 | Development URLs, for local development. 394 | 395 | Frontend: http://local.dockertoolbox.tiangolo.com 396 | 397 | Backend: http://local.dockertoolbox.tiangolo.com/api/ 398 | 399 | Traefik UI: http://local.dockertoolbox.tiangolo.com:8090 400 | 401 | ### Development with a custom IP 402 | 403 | Development URLs, for local development. 404 | 405 | Frontend: http://dev.{{cookiecutter.domain_main}} 406 | 407 | Backend: http://dev.{{cookiecutter.domain_main}}/api/ 408 | 409 | Traefik UI: http://dev.{{cookiecutter.domain_main}}:8090 410 | 411 | ### Development in localhost with a custom domain 412 | 413 | Development URLs, for local development. 414 | 415 | Frontend: http://localhost.tiangolo.com 416 | 417 | Backend: http://localhost.tiangolo.com/api/ 418 | 419 | Traefik UI: http://localhost.tiangolo.com:8090 420 | 421 | ## Project generation and updating, or re-generating 422 | 423 | This project was generated using https://github.com/tiangolo/flask-frontend-docker with: 424 | 425 | ```bash 426 | pip install cookiecutter 427 | cookiecutter https://github.com/tiangolo/flask-frontend-docker 428 | ``` 429 | 430 | You can check the variables used during generation in the file `cookiecutter-config-file.yml`. 431 | 432 | You can generate the project again with the same configurations used the first time. 433 | 434 | That would be useful if, for example, the project generator (`tiangolo/flask-frontend-docker`) was updated and you want to integrate or review the changes. 435 | 436 | You could generate a new project with the same configurations as this one in a parallel directory. And compare the differences between the two, without having to overwrite your current code but being able to use the same variables used for your current project. 437 | 438 | To achieve that, the generated project includes the file `cookiecutter-config-file.yml` with the current variables used. 439 | 440 | You can use that file while generating a new project to reuse all those variables. 441 | 442 | For example, run: 443 | 444 | ```bash 445 | cookiecutter --config-file ./cookiecutter-config-file.yml --output-dir ../project-copy https://github.com/tiangolo/flask-frontend-docker 446 | ``` 447 | 448 | That will use the file `cookiecutter-config-file.yml` in the current directory (in this project) to generate a new project inside a sibling directory `project-copy`. 449 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/backend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tiangolo/uwsgi-nginx-flask:python3.6 2 | 3 | RUN pip install flask_cors 4 | 5 | COPY ./app /app 6 | WORKDIR /app/ 7 | 8 | EXPOSE 80 9 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/backend/app/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiangolo/flask-frontend-docker/05483ff653a7265157b0bfa67421126b91cdc492/{{cookiecutter.project_slug}}/backend/app/app/__init__.py -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/backend/app/app/cors.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask_cors import CORS 4 | 5 | from app.main import app 6 | 7 | BACKEND_CORS_ORIGINS = os.getenv( 8 | "BACKEND_CORS_ORIGINS" 9 | ) # a string of origins separated by commas, e.g: "http://localhost, http://localhost:4200, http://localhost:3000, http://localhost:8080, http://dev.couchbase-project.com, https://stag.couchbase-project.com, https://couchbase-project.com, http://local.dockertoolbox.tiangolo.com" 10 | 11 | origins = [] 12 | 13 | # Set all CORS enabled origins 14 | if BACKEND_CORS_ORIGINS: 15 | origins_raw = BACKEND_CORS_ORIGINS.split(",") 16 | for origin in origins_raw: 17 | use_origin = origin.strip() 18 | origins.append(use_origin) 19 | 20 | CORS(app, origins=origins, supports_credentials=True) 21 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/backend/app/app/main.py: -------------------------------------------------------------------------------- 1 | # Import installed packages 2 | from flask import Flask, jsonify 3 | 4 | # Import app code 5 | app = Flask(__name__) 6 | 7 | # After creating the app, so that cors can import it 8 | from app import cors 9 | 10 | @app.route("/api/") 11 | def root(): 12 | return jsonify({"message": "Hello World"}) 13 | 14 | if __name__ == "__main__": 15 | app.run(debug=True, host="0.0.0.0", port=80) 16 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/backend/app/backend-live.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | export FLASK_APP=app/main.py 3 | export FLASK_DEBUG=1 4 | flask run --host=0.0.0.0 --port=80 5 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/backend/app/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | module = app.main 3 | callable = app 4 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cookiecutter-config-file.yml: -------------------------------------------------------------------------------- 1 | default_context: 2 | project_name: '{{ cookiecutter.project_name }}' 3 | project_slug: '{{ cookiecutter.project_slug }}' 4 | domain_main: '{{ cookiecutter.domain_main }}' 5 | backend_cors_origins: '{{ cookiecutter.backend_cors_origins }}' 6 | docker_swarm_stack_name_main: '{{ cookiecutter.docker_swarm_stack_name_main }}' 7 | traefik_constraint_tag: '{{ cookiecutter.traefik_constraint_tag }}' 8 | traefik_public_network: '{{ cookiecutter.traefik_public_network }}' 9 | traefik_public_constraint_tag: '{{ cookiecutter.traefik_public_constraint_tag }}' 10 | docker_image_prefix: '{{ cookiecutter.docker_image_prefix }}' 11 | docker_image_backend: '{{ cookiecutter.docker_image_backend }}' 12 | docker_image_frontend: '{{ cookiecutter.docker_image_frontend }}' 13 | _copy_without_render: [frontend/src/**/*.html, frontend/src/**/*.vue, frontend/node_modules/*] 14 | _template: ./ 15 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.deploy.build.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | backend: 4 | build: ./backend 5 | frontend: 6 | build: 7 | context: ./frontend 8 | args: 9 | FRONTEND_ENV: ${FRONTEND_ENV-production} 10 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.deploy.command.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | command: --docker \ 5 | --docker.swarmmode \ 6 | --docker.watch \ 7 | --docker.exposedbydefault=false \ 8 | --constraints=tag==${TRAEFIK_TAG} \ 9 | --logLevel=INFO \ 10 | --accessLog \ 11 | --web 12 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.deploy.images.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | backend: 4 | image: '${DOCKER_IMAGE_BACKEND}:${TAG-latest}' 5 | frontend: 6 | image: '${DOCKER_IMAGE_FRONTEND}:${TAG-latest}' 7 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.deploy.labels.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | deploy: 5 | labels: 6 | # For the configured domain 7 | - traefik.frontend.rule=Host:${DOMAIN} 8 | # For a domain with and without 'www' 9 | # Comment the previous line above and un-comment the line below 10 | # - "traefik.frontend.rule=Host:www.${DOMAIN},${DOMAIN}" 11 | - traefik.enable=true 12 | - traefik.port=80 13 | - traefik.tags=${TRAEFIK_PUBLIC_TAG} 14 | - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK} 15 | # Traefik service that listens to HTTP 16 | - traefik.servicehttp.frontend.entryPoints=http 17 | - traefik.servicehttp.frontend.redirect.entryPoint=https 18 | # Traefik service that listens to HTTPS 19 | - traefik.servicehttps.frontend.entryPoints=https 20 | # Uncomment the config line below to detect and redirect www to non-www (or the contrary) 21 | # The lines above for traefik.frontend.rule are needed too 22 | # - "traefik.servicehttps.frontend.redirect.regex=^https?://(www.)?(${DOMAIN})/(.*)" 23 | # To redirect from non-www to www un-comment the line below 24 | # - "traefik.servicehttps.frontend.redirect.replacement=https://www.${DOMAIN}/$$3" 25 | # To redirect from www to non-www un-comment the line below 26 | # - "traefik.servicehttps.frontend.redirect.replacement=https://${DOMAIN}/$$3" 27 | backend: 28 | deploy: 29 | labels: 30 | - traefik.frontend.rule=PathPrefix:/api 31 | - traefik.enable=true 32 | - traefik.port=80 33 | - traefik.tags=${TRAEFIK_TAG} 34 | frontend: 35 | deploy: 36 | labels: 37 | - traefik.frontend.rule=PathPrefix:/ 38 | - traefik.enable=true 39 | - traefik.port=80 40 | - traefik.tags=${TRAEFIK_TAG} 41 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.deploy.networks.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | networks: 5 | - ${TRAEFIK_PUBLIC_NETWORK} 6 | - default 7 | 8 | networks: 9 | {{cookiecutter.traefik_public_network}}: 10 | external: true 11 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.deploy.volumes-placement.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | deploy: 5 | placement: 6 | constraints: 7 | - node.role == manager 8 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.dev.build.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | backend: 4 | build: ./backend 5 | frontend: 6 | build: 7 | context: ./frontend 8 | args: 9 | FRONTEND_ENV: development 10 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.dev.command.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | command: --docker \ 5 | --docker.watch \ 6 | --docker.exposedbydefault=false \ 7 | --constraints=tag==${TRAEFIK_TAG} \ 8 | --logLevel=DEBUG \ 9 | --accessLog \ 10 | --web 11 | # backend: 12 | # command: bash -c "while true; do sleep 1; done" # Infinite loop to keep container live doing nothing 13 | # command: bash -c "flask run --host=0.0.0.0 --port=80" 14 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.dev.labels.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | labels: 5 | - traefik.frontend.rule=Host:${DOMAIN} 6 | - traefik.enable=true 7 | - traefik.port=80 8 | backend: 9 | labels: 10 | - traefik.frontend.rule=PathPrefix:/api 11 | - traefik.enable=true 12 | - traefik.port=80 13 | - traefik.tags=${TRAEFIK_TAG} 14 | frontend: 15 | labels: 16 | - traefik.frontend.rule=PathPrefix:/ 17 | - traefik.enable=true 18 | - traefik.port=80 19 | - traefik.tags=${TRAEFIK_TAG} 20 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.dev.ports.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | ports: 5 | - '80:80' 6 | - '8090:8080' 7 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.dev.volumes.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | backend: 4 | volumes: 5 | - ./backend/app:/app 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.shared.admin.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | proxy: 4 | image: traefik:v1.7 5 | volumes: 6 | - /var/run/docker.sock:/var/run/docker.sock 7 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docker-compose.shared.env.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | backend: 4 | environment: 5 | - BACKEND_CORS_ORIGINS=${BACKEND_CORS_ORIGINS} 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/.env: -------------------------------------------------------------------------------- 1 | VUE_APP_DOMAIN_DEV=localhost 2 | # VUE_APP_DOMAIN_DEV=local.dockertoolbox.tiangolo.com 3 | # VUE_APP_DOMAIN_DEV=localhost.tiangolo.com 4 | # VUE_APP_DOMAIN_DEV=dev.{{cookiecutter.domain_main}} 5 | VUE_APP_DOMAIN_PROD={{cookiecutter.domain_main}} 6 | VUE_APP_NAME={{cookiecutter.project_name}} 7 | VUE_APP_ENV=development 8 | # VUE_APP_ENV=production 9 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | # Stage 0, "build-stage", based on Node.js, to build and compile the frontend 2 | FROM tiangolo/node-frontend:10 as build-stage 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json /app/ 7 | 8 | RUN npm install 9 | 10 | COPY ./ /app/ 11 | 12 | ARG FRONTEND_ENV=production 13 | 14 | ENV VUE_APP_ENV=${FRONTEND_ENV} 15 | 16 | # Un-comment the next line to enable tests after implementing them 17 | # RUN npm run test:unit 18 | 19 | RUN npm run build 20 | 21 | 22 | # Stage 1, based on Nginx, to have only the compiled app, ready for production with Nginx 23 | FROM nginx:1.15 24 | 25 | COPY --from=build-stage /app/dist/ /usr/share/nginx/html 26 | 27 | COPY --from=build-stage /nginx.conf /etc/nginx/conf.d/default.conf 28 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/README.md: -------------------------------------------------------------------------------- 1 | # frontend 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "vue": "^2.5.21" 12 | }, 13 | "devDependencies": { 14 | "@vue/cli-plugin-babel": "^3.2.2", 15 | "@vue/cli-plugin-eslint": "^3.2.2", 16 | "@vue/cli-service": "^3.2.2", 17 | "babel-eslint": "^10.0.1", 18 | "eslint": "^5.8.0", 19 | "eslint-plugin-vue": "^5.0.0", 20 | "vue-template-compiler": "^2.5.21" 21 | }, 22 | "eslintConfig": { 23 | "root": true, 24 | "env": { 25 | "node": true 26 | }, 27 | "extends": [ 28 | "plugin:vue/essential", 29 | "eslint:recommended" 30 | ], 31 | "rules": {}, 32 | "parserOptions": { 33 | "parser": "babel-eslint" 34 | } 35 | }, 36 | "postcss": { 37 | "plugins": { 38 | "autoprefixer": {} 39 | } 40 | }, 41 | "browserslist": [ 42 | "> 1%", 43 | "last 2 versions", 44 | "not ie <= 8" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiangolo/flask-frontend-docker/05483ff653a7265157b0bfa67421126b91cdc492/{{cookiecutter.project_slug}}/frontend/public/favicon.ico -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |
6 | For a guide and recipes on how to configure / customize this project,
7 | check out the
8 | vue-cli documentation.
9 |