├── .editorconfig ├── .gitignore ├── README.md ├── app ├── Dockerfile ├── apps │ ├── auth │ │ ├── __init__.py │ │ ├── bp.py │ │ └── models.py │ └── readme.md ├── auto.py ├── config.py ├── entrypoint.sh ├── extensions.py ├── main.py ├── tox.ini └── utils.py ├── docker-compose-dev.yml ├── docker-compose-prd.yml ├── docker-compose-tst.yml ├── docker-compose.yml ├── envfile ├── envfile-dev ├── envfile-tst ├── fabfile.py ├── server ├── Dockerfile ├── entrypoint.sh ├── nginx-dev.conf └── nginx-prd.conf ├── styles ├── .dockerignore ├── Dockerfile ├── dist │ └── .keepme ├── entrypoint.sh ├── package.json └── semantic.json └── ux ├── .dockerignore ├── Dockerfile ├── dist └── .keepme ├── entrypoint.sh └── src └── styles └── .keepme /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space 6 | 7 | [*.py] 8 | insert_final_newline = true 9 | indent_size = 4 10 | 11 | [Makefile] 12 | indent_style = tab -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .v8flags* 3 | .vscode 4 | .bash_history 5 | node_modules 6 | dist 7 | secrets -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | Setting up **docker** + **nginx** + **flask** + **vue** + **semantic** is hardly an easy task if you want to setup production and development environments. This is my attempt 4 | in the matter. Notice that, some containers are not complete (ux) 5 | because, well, vue does not have a non interactive install. 6 | 7 | ## Getting started 8 | 9 | ```bash 10 | # make sure you have fabric3 installed 11 | # make sure to read each folder readme 12 | # make sure you have docker and docker-compose installed 13 | 14 | # call setup to install dependencies 15 | fab setup 16 | 17 | # now you're ready to go 18 | fab env:dev up # docker-compose up in development mode 19 | fab env:prd up # docker-compose up in production mode 20 | fab env:dev build # docker-compose build in development mode 21 | fab env:prd build # docker-compose build in production mode 22 | fab env:dev on: run:"" # docker-compose run in development mode 23 | fab env:prd on: run:"" # docker-compose run in production mode 24 | fab env:dev logs:name # docker logs on container called 25 | fab env:prd logs:name # docker logs on container called 26 | ``` 27 | 28 | ## What is what? 29 | 30 | * app -> flask application container 31 | * server -> nginx container 32 | * styles -> semantic-ui container 33 | * ux -> vuejs application container 34 | 35 | ## Trouble? 36 | 37 | As webpack is a every-changing beast, leave a issue at 38 | https://github.com/italomaia/flask-vue-semantic-docker/issues if 39 | something doesn't work. There might have been a change in a 40 | dependency at some point. 41 | 42 | ## Changelog 43 | 44 | **0.3.1** 45 | 46 | * Added more feedback on dependencies 47 | 48 | **0.3.0** 49 | 50 | * Nothing, really 51 | 52 | **0.2.8** 53 | 54 | * Fixed semantic files import 55 | 56 | **0.2.7** 57 | 58 | * Flask "app" is now built inside $HOME/code to avoid bash session dirt 59 | * Vue.JS "ux" is now built inside $HOME/code to avoid bash session dirt 60 | * Fixed entrypoint.sh permissions for mounted folders 61 | * Added entrypoint.sh to app/ 62 | 63 | **0.2.6** 64 | 65 | * Fixed source reload on ux code change during dev 66 | 67 | **0.2.5** 68 | 69 | * Readme instructions are now configured by default 70 | 71 | **0.2.4** 72 | 73 | * webpack dev server endpoints have changed. Updated, so nginx can route to them. 74 | * version bump for docker base images (node's, actually) 75 | 76 | **0.2.3** 77 | 78 | * Added `on:` task; it is used to pick which service your command is run against. Right now, only works with `run`. 79 | 80 | **0.2.2** 81 | 82 | * Added adminer for dev 83 | * Added logs command to fabfile 84 | * Added run command to fabfile (docker-compose run) 85 | * Small fixes 86 | 87 | **0.2.1** 88 | 89 | * installed extensions are now properly loaded 90 | * added basic "auth" app implementation (for authentication) 91 | * added some sensitive defaults for sqlalchemy configuration 92 | 93 | **0.2** 94 | 95 | * update to flask app dependencies (+flask-jsglue +flask-marshmallow +flask-migrate +flask-security +flask-sqlalchemy) 96 | 97 | **0.1** 98 | 99 | * initial version (docker + flask + vuejs + semantic-ui) 100 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | ENV USR nonroot 4 | ENV HOME /home/${USR} 5 | ENV PROJECT_DIR ${HOME}/code 6 | ENV PYTHONUNBUFFERED 1 7 | 8 | EXPOSE 5000 9 | 10 | RUN groupadd -g 1000 -r ${USR} && \ 11 | useradd -u 1000 -d $HOME -m -r -g ${USR} ${USR} 12 | 13 | RUN pip install --no-cache-dir \ 14 | empty==0.4.3\ 15 | eventlet==0.21.0\ 16 | flask-jsglue==0.3.1\ 17 | flask-marshmallow==0.8.0\ 18 | flask-migrate==2.0.4\ 19 | flask-restful==0.3.6\ 20 | flask-security==3.0.0\ 21 | flask-socketio==2.8.6\ 22 | flask-sqlalchemy==2.2\ 23 | flask-rq2\ 24 | marshmallow-sqlalchemy==0.13.1\ 25 | psycopg2==2.7.1\ 26 | redis==2.10.5 27 | 28 | USER ${USR} 29 | RUN mkdir ${PROJECT_DIR} 30 | WORKDIR ${PROJECT_DIR} 31 | COPY --chown=nonroot:nonroot . . 32 | 33 | CMD ["flask", "run", "-h", "0.0.0.0"] -------------------------------------------------------------------------------- /app/apps/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from .bp import app # noqa 2 | -------------------------------------------------------------------------------- /app/apps/auth/bp.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | app = Blueprint('auth', __name__, template_folder=None) 4 | -------------------------------------------------------------------------------- /app/apps/auth/models.py: -------------------------------------------------------------------------------- 1 | from extensions import db 2 | from flask_security import SQLAlchemyUserDatastore 3 | from flask_security import UserMixin, RoleMixin 4 | 5 | # Define models 6 | roles_users = db.Table( 7 | 'roles_users', 8 | db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), 9 | db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))) 10 | 11 | 12 | class Role(db.Model, RoleMixin): 13 | id = db.Column(db.Integer(), primary_key=True) 14 | name = db.Column(db.String(80), unique=True) 15 | description = db.Column(db.String(255)) 16 | 17 | 18 | class User(db.Model, UserMixin): 19 | id = db.Column(db.Integer, primary_key=True) 20 | email = db.Column(db.String(255), unique=True) 21 | password = db.Column(db.String(255)) 22 | active = db.Column(db.Boolean()) 23 | confirmed_at = db.Column(db.DateTime()) 24 | roles = db.relationship( 25 | 'Role', 26 | secondary=roles_users, 27 | backref=db.backref('users', lazy='dynamic')) 28 | 29 | 30 | # Setup Flask-Security 31 | user_datastore = SQLAlchemyUserDatastore(db, User, Role) 32 | -------------------------------------------------------------------------------- /app/apps/readme.md: -------------------------------------------------------------------------------- 1 | Place your blueprints packages here. -------------------------------------------------------------------------------- /app/auto.py: -------------------------------------------------------------------------------- 1 | import eventlet 2 | eventlet.monkey_patch() 3 | 4 | from empty import app_factory 5 | from main import App 6 | import config 7 | import traceback 8 | import logging 9 | 10 | try: 11 | # SPA setup; template_folder ignored; 12 | app = app_factory( 13 | 'app', config, 14 | template_folder=None, 15 | base_application=App) 16 | except Exception as e: 17 | logging.error(traceback.format_exc()) 18 | raise e 19 | -------------------------------------------------------------------------------- /app/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from utils import load_env 3 | 4 | DEBUG = os.getenv('FLASK_DEBUG') == '1' 5 | SECRET_KEY = load_env('FLASK_SECRET_KEY') 6 | 7 | BLUEPRINTS = ['auth'] 8 | EXTENSIONS = list(map(lambda e: 'extensions.' + e, [ 9 | 'io', 10 | 'db', 11 | 'migrate', 12 | 'glue', 13 | 'ma', 14 | 'security' 15 | ])) 16 | 17 | # Make sure SERVER_NAME contains the access port for 18 | # the http server if it is not a default port (ex: dv:8080) 19 | # Also, add "127.0.0.1 dv" to your /etc/hosts during development 20 | SERVER_NAME = os.getenv('SERVER_NAME') + os.getenv('SERVER_NAME_EXTRA', '') 21 | 22 | PSYCOPG2_URI = 'postgresql+psycopg2://{user}:{passwd}@{host}/{name}' 23 | SQLALCHEMY_DATABASE_URI = PSYCOPG2_URI.format( 24 | user=load_env('POSTGRES_USER'), 25 | passwd=load_env('POSTGRES_PASSWORD'), 26 | host='db', 27 | name=load_env('POSTGRES_DB') 28 | ) 29 | SQLALCHEMY_TRACK_MODIFICATIONS = False 30 | -------------------------------------------------------------------------------- /app/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | exec "$@" -------------------------------------------------------------------------------- /app/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_socketio import SocketIO 2 | from flask_sqlalchemy import SQLAlchemy 3 | from flask_migrate import Migrate 4 | from flask_jsglue import JSGlue 5 | from flask_marshmallow import Marshmallow 6 | from flask_security import Security 7 | 8 | io = SocketIO() 9 | db = SQLAlchemy() 10 | migrate = Migrate(db=db) 11 | glue = JSGlue() 12 | ma = Marshmallow() 13 | security = Security() 14 | 15 | 16 | def security_init_kwargs(): 17 | from auth.models import user_datastore 18 | return dict(datastore=user_datastore) 19 | 20 | 21 | def io_init_kwargs(): 22 | return dict(logger=True, engineio_logger=True) 23 | -------------------------------------------------------------------------------- /app/main.py: -------------------------------------------------------------------------------- 1 | from empty import Empty 2 | 3 | 4 | class App(Empty): 5 | def configure_views(self): 6 | @self.route('/api/hello') 7 | def index(): 8 | """Use this to make sure your web app is reachable""" 9 | return 'It Works' 10 | 11 | def configure_error_handlers(self): 12 | """SPA""" 13 | pass 14 | 15 | 16 | if __name__ == '__main__': 17 | from auto import app 18 | from extensions import io 19 | 20 | io.run(app) 21 | -------------------------------------------------------------------------------- /app/tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore=E402 -------------------------------------------------------------------------------- /app/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def load_env(name): 5 | """ 6 | Tries to load name from environment variables by name; if 7 | it is not set, appends "_FILE" to `name` and tries to load 8 | it from filesytem. This is quite useful for loading docker 9 | secrets. 10 | """ 11 | value = os.getenv(name) 12 | if value is None: 13 | value = os.getenv('%s_FILE' % name) 14 | if value is not None and os.path.exists(value): 15 | with open(value) as fs: 16 | return fs.read() 17 | return value 18 | -------------------------------------------------------------------------------- /docker-compose-dev.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | services: 4 | db: 5 | env_file: 6 | - ./envfile-dev 7 | app: 8 | env_file: 9 | - ./envfile-dev 10 | ports: 11 | - 5000:5000 12 | volumes: 13 | - type: bind 14 | source: ./app 15 | target: /home/nonroot/code 16 | ux: 17 | command: ["npm", "run", "dev"] 18 | entrypoint: ["./entrypoint.sh"] 19 | env_file: 20 | - ./envfile-dev 21 | volumes: 22 | - type: bind 23 | source: ./ux 24 | target: /home/node/code 25 | styles: 26 | command: sh -c "gulp --cwd=semantic build && gulp --cwd=semantic watch" 27 | env_file: 28 | - ./envfile-dev 29 | volumes: 30 | - type: bind 31 | source: ./styles/semantic 32 | target: /home/node/semantic 33 | server: 34 | command: ["nginx-debug", "-g", "daemon off;"] 35 | entrypoint: ["./entrypoint.sh"] 36 | env_file: 37 | - ./envfile-dev 38 | links: 39 | - ux 40 | ports: 41 | - 8080:80 42 | - 8081:443 -------------------------------------------------------------------------------- /docker-compose-prd.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | # ports are not attached to host by default 4 | # you're advised to use a proxy for your container 5 | # see: https://github.com/jwilder/nginx-proxy 6 | # also: https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion 7 | # also: https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion 8 | # also: https://github.com/italomaia/reverse-proxy (all stitched up proxy solution) 9 | 10 | secrets: 11 | flask_secret_key: 12 | file: ./secrets/flask_secret_key 13 | postgres_usr: 14 | file: ./secrets/postgres_usr 15 | postgres_pwd: 16 | file: ./secrets/postgres_pwd 17 | 18 | services: 19 | server: 20 | volumes: 21 | - type: volume 22 | source: ux-dist 23 | target: /usr/share/nginx/html 24 | read_only: true -------------------------------------------------------------------------------- /docker-compose-tst.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | services: 4 | app: 5 | env_file: 6 | - ./envfile-dev 7 | - ./envfile-tst 8 | server: 9 | env_file: 10 | - ./envfile-dev 11 | - ./envfile-tst 12 | ports: 13 | - 8080:80 14 | - 8081:443 15 | volumes: 16 | - type: volume 17 | source: ux-dist 18 | target: /usr/share/nginx/html 19 | read_only: true 20 | ux: 21 | env_file: 22 | - ./envfile-dev 23 | - ./envfile-tst 24 | styles: 25 | env_file: 26 | - ./envfile-dev 27 | - ./envfile-tst -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | volumes: 4 | db-data: 5 | driver: local 6 | ux-dist: 7 | driver: local 8 | styles-dist: 9 | driver: local 10 | 11 | services: 12 | styles: 13 | build: ./styles 14 | command: ["gulp", "--cwd=semantic", "build"] 15 | entrypoint: ["./entrypoint.sh"] 16 | env_file: 17 | - ./envfile 18 | volumes: 19 | - type: volume 20 | source: styles-dist 21 | target: /home/node/dist/ 22 | ux: 23 | build: ./ux 24 | command: ["npm", "run", "build"] 25 | entrypoint: ["./entrypoint.sh"] 26 | env_file: 27 | - ./envfile 28 | volumes: 29 | - type: volume 30 | source: ux-dist 31 | target: /home/node/code/dist/ 32 | - type: volume 33 | source: styles-dist 34 | target: /home/node/code/src/styles/ 35 | read_only: true 36 | db: 37 | image: postgres:10 38 | env_file: 39 | - ./envfile 40 | volumes: 41 | - db-data:/var/lib/postgresql/data/ 42 | app: 43 | build: ./app 44 | env_file: 45 | - ./envfile 46 | links: 47 | - db 48 | server: 49 | build: ./server 50 | env_file: 51 | - ./envfile 52 | links: 53 | - app -------------------------------------------------------------------------------- /envfile: -------------------------------------------------------------------------------- 1 | # change by your app name 2 | # do not keep this in vcs 3 | PGDATA=/var/lib/postgresql/data/ 4 | POSTGRES_DB= 5 | POSTGRES_USER=_user 6 | POSTGRES_PASSWORD=passw0rd 7 | 8 | FLASK_APP=auto.py 9 | FLASK_DEBUG=0 10 | # generate your secret with the command below 11 | # python -c "import os; print(os.urandom(20))" 12 | FLASK_SECRET=secret 13 | FLASK_LOGGER_NAME=_logger 14 | 15 | # SERVER_NAME should be the full domain 16 | # serving your application 17 | SERVER_NAME= 18 | NGINX_TEMPLATE=nginx-prd.conf -------------------------------------------------------------------------------- /envfile-dev: -------------------------------------------------------------------------------- 1 | PGDATA=/var/lib/postgresql/data/app-dev 2 | POSTGRES_DB=app 3 | POSTGRES_USER=app_user 4 | POSTGRES_PASSWORD=passw0rd 5 | 6 | FLASK_APP=auto.py 7 | FLASK_DEBUG=1 8 | FLASK_SECRET=secret 9 | FLASK_LOGGER_NAME=app_logger 10 | 11 | # ux dev configuration 12 | HOST=0.0.0.0 13 | PORT=8080 14 | 15 | SERVER_NAME=dv 16 | SERVER_NAME_EXTRA=:8080 17 | NGINX_TEMPLATE=nginx-dev.conf -------------------------------------------------------------------------------- /envfile-tst: -------------------------------------------------------------------------------- 1 | FLASK_DEBUG=0 2 | 3 | SERVER_NAME=dv 4 | SERVER_NAME_EXTRA=:8080 5 | NGINX_TEMPLATE=nginx-prd.conf -------------------------------------------------------------------------------- /fabfile.py: -------------------------------------------------------------------------------- 1 | from fabric.api import env 2 | from fabric.api import local 3 | from fabric.api import run 4 | from fabric.api import task 5 | from fabric.context_managers import cd, lcd 6 | 7 | import os 8 | import json 9 | 10 | env.forward_agent = True 11 | env.user = 'root' 12 | env.hosts = ['your production host'] 13 | 14 | project_dst = 'project-name' 15 | 16 | compose_cmd = [ 17 | 'docker-compose', 18 | '-f', 'docker-compose.yml', 19 | '-f', 20 | ] 21 | 22 | # service to run commands against 23 | service_name = None 24 | renv = 'dev' # dev by default 25 | CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) 26 | STYLES_DIR = os.path.join(CURRENT_DIR, 'styles') 27 | UX_DIR = os.path.join(CURRENT_DIR, 'ux') 28 | APP_DIR = os.path.join(CURRENT_DIR, 'app') 29 | 30 | 31 | def get_compose_cmd(): 32 | return compose_cmd + ['docker-compose-%s.yml' % renv] 33 | 34 | 35 | def get_fn(): 36 | """ 37 | Returns the correct function call for the environment. 38 | """ 39 | return run if renv == 'prd' else local 40 | 41 | 42 | def get_cmd_exists(cmd): 43 | def tell_on(arg, rs): 44 | if rs: 45 | print('"%s" found in path.' % arg) 46 | else: 47 | print('"%s" not found in path. Please, install it to continue.' % arg) # noqa 48 | return rs 49 | 50 | fn = get_fn() 51 | rs = fn('which %s' % cmd, capture=True) 52 | return tell_on(cmd, ('not found' not in rs)) 53 | 54 | 55 | def insert_line_after(lines, line, after): 56 | for i in range(len(lines)): 57 | if after in lines[i]: 58 | lines.insert(i+1, line) 59 | break 60 | 61 | 62 | def replace_line(lines, line, condition): 63 | for i in range(len(lines)): 64 | if condition in lines[i]: 65 | lines[i] = line 66 | break 67 | 68 | 69 | def update_webpack_config(path): 70 | with open(path) as fs: 71 | lines = fs.readlines() 72 | 73 | line_to_insert = " poll: 800,\n" 74 | line_condition = "poll: false" 75 | replace_line(lines, line_to_insert, line_condition) 76 | 77 | with open(path, 'w') as fs: 78 | fs.write(''.join(lines)) 79 | 80 | 81 | def update_webpack_base_conf(conf_path): 82 | with open(conf_path) as fs: 83 | lines = fs.readlines() 84 | 85 | line_to_insert = ""\ 86 | " plugins: [\n"\ 87 | " new webpack.ProvidePlugin({\n"\ 88 | " '$': 'jquery',\n"\ 89 | " 'jQuery': 'jquery',\n"\ 90 | " 'window.jQuery': 'jquery'\n"\ 91 | " })],\n" 92 | line_condition = 'module.exports = {' 93 | insert_line_after(lines, line_to_insert, line_condition) 94 | 95 | line_to_insert = " exclude: [resolve('src/styles')],\n" 96 | line_condition = "include: [resolve('src'), resolve('test')]" 97 | insert_line_after(lines, line_to_insert, line_condition) 98 | 99 | line_to_insert = "const webpack = require('webpack')\n" 100 | line_condition = "const vueLoaderConfig" 101 | insert_line_after(lines, line_to_insert, line_condition) 102 | 103 | with open(conf_path, 'w') as fs: 104 | fs.write(''.join(lines)) 105 | 106 | 107 | def update_webpack_dev_conf(conf_path): 108 | with open(conf_path) as fs: 109 | lines = fs.readlines() 110 | 111 | # add disable host check; required for development with webpack 112 | line_to_insert = ' disableHostCheck: true,\n' 113 | line_condition = 'devServer: {' 114 | insert_line_after(lines, line_to_insert, line_condition) 115 | 116 | with open(conf_path, 'w') as fs: 117 | fs.write(''.join(lines)) 118 | 119 | 120 | def update_ux_main(path): 121 | with open(path) as fs: 122 | lines = fs.readlines() 123 | 124 | line_to_insert = "\n"\ 125 | "require('./styles/semantic.css')\n"\ 126 | "require('./styles/semantic.js')\n" 127 | line_condition = "productionTip" 128 | insert_line_after(lines, line_to_insert, line_condition) 129 | 130 | with open(path, 'w') as fs: 131 | fs.write(''.join(lines)) 132 | 133 | 134 | @task(alias='setup') 135 | def do_setup(): 136 | """ 137 | Helps you setup your environment. Call it once per project. 138 | """ 139 | msg = "Command not found. Please, install %s" 140 | assert get_cmd_exists('npm'), msg % "npm" 141 | assert get_cmd_exists('gulp'), msg % "gulp" 142 | assert get_cmd_exists('yarn'), msg % "yarn" 143 | assert get_cmd_exists('vue'), msg % "vue-cli" 144 | assert get_cmd_exists('fab'), msg % "fabric3" 145 | assert get_cmd_exists('docker'), msg % "docker" 146 | assert get_cmd_exists('docker-compose'), msg % "docker-compose" 147 | 148 | with lcd(APP_DIR): 149 | # make sure entrypoint has execution permission 150 | # so that the development environment doesn't break 151 | local('chmod +x entrypoint.sh') 152 | 153 | # do not change the directory context here 154 | local('vue init webpack ux', shell='/bin/bash') 155 | 156 | with lcd(UX_DIR): 157 | print("Setting up VueJS (just accept defaults)") 158 | 159 | # make sure entrypoint has execution permission 160 | # so that the development environment doesn't break 161 | local('chmod +x entrypoint.sh') 162 | 163 | update_webpack_config("ux/config/index.js") 164 | update_webpack_base_conf("ux/build/webpack.base.conf.js") 165 | update_webpack_dev_conf("ux/build/webpack.dev.conf.js") 166 | update_ux_main("ux/src/main.js") 167 | 168 | local("yarn add jquery") 169 | 170 | with lcd(STYLES_DIR): 171 | print("Setting up SemanticUI (just accept defaults)") 172 | 173 | # make sure entrypoint has execution permission 174 | # so that the development environment doesn't break 175 | local('chmod +x entrypoint.sh') 176 | local('npm install semantic-ui', shell='/bin/bash') 177 | 178 | semantic_settings = os.path.join(STYLES_DIR, 'semantic.json') 179 | with open(semantic_settings, 'r') as fs: 180 | data = json.load(fs) 181 | 182 | data['autoInstall'] = True 183 | with open(semantic_settings, 'w') as fs: 184 | json.dump(data, fs) 185 | 186 | print( 187 | "IMPORTANT: run the following command:\n" 188 | "sudo echo \"127.0.0.1 dv\" >> /etc/hosts") 189 | 190 | print( 191 | "IMPORTANT: make sure to update your envfile file with " 192 | "your project production configuration.") 193 | print( 194 | "IMPORTANT: make sure to update your fabfile " 195 | "hosts with your production host.") 196 | print("") 197 | print("Now you're ready to go:") 198 | print(' fab env:dev up # for development mode') 199 | print(' fab env:prd up # for production mode') 200 | print(' fab env:tst up # to simulate production mode') 201 | print('Locally, your project will be available at http://dv:8080') 202 | 203 | 204 | @task(alias='env') 205 | def set_renv(local_renv): 206 | "Sets docker-compose environment" 207 | global renv 208 | assert local_renv in ('dev', 'prd') 209 | renv = local_renv 210 | 211 | 212 | @task(alias='up') 213 | def compose_up(name=None): 214 | """ 215 | Calls docker compose up using the correct environment. 216 | """ 217 | opt = ['-d'] if renv == 'prd' else [] 218 | 219 | with cd(project_dst): 220 | local_cmd = get_compose_cmd() + ['up'] 221 | local_cmd += opt 222 | local_cmd += [name] if name else [] 223 | get_fn()(' '.join(local_cmd)) 224 | 225 | 226 | @task(alias='build') 227 | def compose_build(name=None): 228 | """ 229 | Calls docker compose build using the correct environment. 230 | """ 231 | with cd(project_dst): 232 | local_cmd = get_compose_cmd() + ['build'] 233 | local_cmd += [name] if name else [] 234 | 235 | get_fn()(' '.join(local_cmd)) 236 | 237 | 238 | @task(alias='on') 239 | def on_service(name): 240 | """ 241 | Define service where command should run 242 | """ 243 | global service_name 244 | service_name = name 245 | 246 | 247 | @task(alias='run') 248 | def compose_run(cmd): 249 | """ 250 | Calls docker compose run using the correct environment. 251 | 252 | :param cmd: run command, including container name. 253 | """ 254 | opt = ['--rm'] 255 | 256 | if service_name is None: 257 | print("please, provide service name") 258 | exit() 259 | 260 | with cd(project_dst): 261 | local_cmd = get_compose_cmd() + ['run'] 262 | local_cmd += opt 263 | local_cmd += [service_name] 264 | local_cmd += cmd.split() 265 | get_fn()(' '.join(local_cmd)) 266 | 267 | 268 | @task(alias='logs') 269 | def docker_logs(name): 270 | """ 271 | Get docker container logs. 272 | """ 273 | get_fn()('docker logs %s' % name) 274 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1 2 | 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | ENV DOLLAR $ 7 | 8 | COPY . . 9 | RUN chmod +x entrypoint.sh 10 | CMD ["nginx", "-g", "daemon off;"] 11 | ENTRYPOINT ["./entrypoint.sh"] -------------------------------------------------------------------------------- /server/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | envsubst < $NGINX_TEMPLATE > /etc/nginx/conf.d/default.conf 4 | exec "$@" -------------------------------------------------------------------------------- /server/nginx-dev.conf: -------------------------------------------------------------------------------- 1 | upstream io { 2 | ip_hash; 3 | # ip_hash is required to use socket io 4 | 5 | server app:5000; 6 | # to scale the app, just add more nodes here! 7 | } 8 | 9 | upstream webapp { 10 | server app:5000; 11 | # to scale the app, just add more nodes here! 12 | } 13 | 14 | server { 15 | listen 80; 16 | listen [::]:80 ipv6only=on; 17 | 18 | listen 443; 19 | listen [::]:443 ipv6only=on; 20 | 21 | server_name ${SERVER_NAME}; 22 | 23 | location /static { 24 | proxy_set_header Host ${DOLLAR}http_host; 25 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr; 26 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for; 27 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme; 28 | 29 | proxy_pass http://ux:8080/static; 30 | } 31 | 32 | location /socket.io { 33 | proxy_set_header Host ${DOLLAR}http_host; 34 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr; 35 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for; 36 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme; 37 | 38 | proxy_http_version 1.1; 39 | proxy_buffering off; 40 | proxy_set_header Upgrade ${DOLLAR}http_upgrade; 41 | proxy_set_header Connection "Upgrade"; 42 | proxy_pass http://io/socket.io; 43 | } 44 | 45 | location /api { 46 | proxy_set_header Host ${DOLLAR}http_host; 47 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr; 48 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for; 49 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme; 50 | 51 | proxy_pass http://webapp/api; 52 | } 53 | 54 | location / { 55 | proxy_set_header Host ${DOLLAR}http_host; 56 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr; 57 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for; 58 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme; 59 | 60 | proxy_http_version 1.1; 61 | proxy_buffering off; 62 | proxy_set_header Upgrade ${DOLLAR}http_upgrade; 63 | proxy_set_header Connection "Upgrade"; 64 | 65 | proxy_pass http://ux:8080; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /server/nginx-prd.conf: -------------------------------------------------------------------------------- 1 | upstream io { 2 | ip_hash; 3 | # ip_hash is required to use socket io 4 | 5 | server app:5000; 6 | # to scale the app, just add more nodes here! 7 | } 8 | 9 | upstream webapp { 10 | server app:5000; 11 | # to scale the app, just add more nodes here! 12 | } 13 | 14 | server { 15 | listen 80 default_server; 16 | listen [::]:80 default_server; 17 | 18 | listen 443 ssl http2; 19 | listen [::]:443 ipv6only=on ssl http2; 20 | 21 | server_name ${SERVER_NAME}; 22 | root /usr/share/nginx/html; 23 | 24 | # serves index.html 25 | location = / { try_files /index.html =404; } 26 | # serves favicon 27 | location = /favicon.ico { } 28 | # serves all static files under the static folder 29 | location /static { autoindex off; } 30 | 31 | location /socket.io { 32 | proxy_set_header Host ${DOLLAR}http_host; 33 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr; 34 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for; 35 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme; 36 | 37 | proxy_http_version 1.1; 38 | proxy_buffering off; 39 | proxy_set_header Upgrade ${DOLLAR}http_upgrade; 40 | proxy_set_header Connection "Upgrade"; 41 | proxy_pass http://io/socket.io; 42 | } 43 | 44 | location / { 45 | proxy_set_header Host ${DOLLAR}http_host; 46 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr; 47 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for; 48 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme; 49 | 50 | proxy_pass http://webapp; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /styles/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /styles/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:9-slim 2 | 3 | ENV USR node 4 | ENV HOME /home/${USR} 5 | 6 | RUN yarn global add gulp --non-interactive --no-progress --no-lockfile && \ 7 | yarn cache clean --force 8 | 9 | USER ${USR} 10 | WORKDIR ${HOME} 11 | 12 | COPY --chown=node:node . . 13 | RUN chmod +x entrypoint.sh 14 | 15 | RUN yarn add semantic-ui --ignore-scripts --non-interactive --no-progress --no-lockfile && \ 16 | yarn cache clean --force -------------------------------------------------------------------------------- /styles/dist/.keepme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/italomaia/flask-vue-semantic-docker/c13612d57b7d5866ed867ab0ba75a987c374ec08/styles/dist/.keepme -------------------------------------------------------------------------------- /styles/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | exec "$@" -------------------------------------------------------------------------------- /styles/package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /styles/semantic.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "semantic/", 3 | "paths": { 4 | "source": { 5 | "config": "src/theme.config", 6 | "definitions": "src/definitions/", 7 | "site": "src/site/", 8 | "themes": "src/themes/" 9 | }, 10 | "output": { 11 | "packaged": "../dist/", 12 | "uncompressed": "../dist/components/", 13 | "compressed": "../dist/components/", 14 | "themes": "../dist/themes/" 15 | }, 16 | "clean": "../dist/" 17 | }, 18 | "permission": false, 19 | "autoInstall": false, 20 | "rtl": false, 21 | "version": "2.2.11" 22 | } -------------------------------------------------------------------------------- /ux/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /ux/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:9 2 | 3 | ENV USR node 4 | ENV HOME /home/${USR} 5 | ENV PROJECT_DIR ${HOME}/code 6 | 7 | # make sure project_dir exists before moving into it 8 | USER ${USR} 9 | RUN mkdir ${PROJECT_DIR} 10 | WORKDIR ${PROJECT_DIR} 11 | COPY --chown=node:node . . 12 | 13 | RUN chmod +x entrypoint.sh 14 | RUN yarn --non-interactive --no-progress --no-lockfile && \ 15 | yarn cache clean 16 | -------------------------------------------------------------------------------- /ux/dist/.keepme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/italomaia/flask-vue-semantic-docker/c13612d57b7d5866ed867ab0ba75a987c374ec08/ux/dist/.keepme -------------------------------------------------------------------------------- /ux/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | exec "$@" -------------------------------------------------------------------------------- /ux/src/styles/.keepme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/italomaia/flask-vue-semantic-docker/c13612d57b7d5866ed867ab0ba75a987c374ec08/ux/src/styles/.keepme --------------------------------------------------------------------------------