├── .gitignore ├── .travis.yml ├── README.md ├── cookiecutter.json ├── hooks └── post_gen_project.py ├── req-packages.txt ├── req-test.txt ├── tests ├── pytest.ini └── test.py └── {{cookiecutter.repo_name}} ├── .eslintrc ├── .secret ├── .src_gitignore ├── buildall.sh ├── container_shared └── .init ├── docker-cleanup.sh ├── docker-compose.yml ├── docker ├── base │ ├── Dockerfile │ ├── runutils.py │ └── skel │ │ ├── .bashrc │ │ └── .vimrc ├── django-python3 │ ├── Dockerfile │ └── config │ │ ├── run.py │ │ └── uwsgi.conf ├── nginx │ ├── Dockerfile │ └── config │ │ ├── nginx.conf │ │ ├── nginx_mime.types │ │ ├── run.py │ │ └── uwsgi_params ├── nodejs │ ├── Dockerfile │ └── config │ │ └── run.py └── postgres │ ├── Dockerfile │ └── config │ ├── pg_hba.conf │ ├── postgresql.conf │ └── run.py ├── production-docker-compose.yml ├── react ├── components │ ├── app.jsx │ ├── home.jsx │ └── router.jsx └── package.json └── src ├── core ├── __init__.py ├── management │ └── commands │ │ ├── __init__.py │ │ └── makemessages.py ├── settings.py ├── static │ ├── css │ │ └── cover.css │ └── react │ │ └── .init ├── templates │ ├── admin │ │ └── base_site.html │ ├── home.html │ └── rosetta │ │ ├── base.html │ │ ├── languages.html │ │ └── pofile.html ├── urls.py ├── views.py └── wsgi.py └── manage.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[oc] 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | before_install: 5 | - pip install codecov 6 | install: pip install -r req-test.txt 7 | language: python 8 | python: 9 | - "3.5" 10 | script: py.test --cov=. tests/ 11 | after_success: 12 | - codecov 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-docker-bootstrap 2 | [![Requirements Status](https://requires.io/github/legios89/django-docker-bootstrap/requirements.svg?branch=master)](https://requires.io/github/legios89/django-docker-bootstrap/requirements/?branch=master) 3 | [![Build Status](https://travis-ci.org/legios89/django-docker-bootstrap.svg?branch=master)](https://travis-ci.org/legios89/django-docker-bootstrap) 4 | [![Dependency Status](https://david-dm.org/legios89/django-docker-bootstrap/master.svg?path={{cookiecutter.repo_name}}/react/)](https://david-dm.org/legios89/django-docker-bootstrap/?path={{cookiecutter.repo_name}}/react/) 5 | ![postgres](https://img.shields.io/badge/postgres-9.4-brightgreen.svg) 6 | ![nginx](https://img.shields.io/badge/nginx-1.8.1-brightgreen.svg) 7 | 8 | ## Concept 9 | A [`cookiecutter`](https://github.com/audreyr/cookiecutter) template for Django/Nodejs(React)/Nginx/Postgres. The main idea behind this project to create an easily configurable and easy to use django development/production environment for any project. 10 | 11 | ## Installation 12 | * Docker requires a 64-bit OS. 13 | * Install the docker(1.12.1) - https://docs.docker.com/engine/installation/ 14 | * docker-compose(1.8.0) - ```pip install docker-compose``` 15 | * Requirements for cookiecutter: ```apt-get install python-dev``` 16 | * Then get cookiecutter: ```pip install Markdown cookiecutter``` 17 | * Finally enter the directory where you want to store your project and enter the following: 18 | * ```cookiecutter https://github.com/legios89/django-docker-bootstrap.git``` 19 | 20 | ## Cookiecutter params 21 | * project_name: The name of the project. 22 | * repo_name: The directory name(automtically generated from the project_name). 23 | * db_password: The postgres user password for the database. 24 | * use_translation: Translation integration(Rosetta, ModelTransalation, admin integration). 25 | * use_react: React, NodeJS, React-Router to replace the django frontend. 26 | * admin_name: The name of the admin user who will receive the error messages. 27 | * admin_email: The email address of the admin user who will receive the error messages. 28 | * email_host_user: The gmail email address what the system can use to send emails. 29 | * email_host_password: The gmail email address password what the system can use to send emails. 30 | 31 | ## Usage 32 | * Build the images: ```bash buildall.sh``` 33 | * Start the project: ```docker-compose up ``` 34 | * You can set every secret variable in the ```.secret``` in the root 35 | * If you want to run the project in production mode you need to set the following environment variable: 36 | * ```COMPOSE_FILE="production-docker-compose.yml"``` 37 | * https://docs.docker.com/compose/reference/overview/#compose-file 38 | 39 | ## Tips & Tricks 40 | * Every image has a container_shared directory linked as a volume, so if you want to put something inside the container, or 41 | you want to get something from the containers like a backup file you just need to copy everything into this directory. 42 | * Create a bash alias for for the docker-compose by edit the ```.bash_aliases``` file ```alias dc='docker-compose'``` 43 | * Enter the container as root: ```dc run --rm postgres shell root``` 44 | * You can use vim in every container. 45 | * https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04 46 | * better prompt: https://github.com/vertisfinance/gitprompt 47 | 48 | ## Volumes 49 | - The data volume, automatically created on the first start, which will contains every data. 50 | - You can find it in every container in here: ```/data/``` 51 | 52 | ## Images 53 | 1. postgres 54 | * postgresql-9.4 55 | * The 5433 port is open by default if you want to connect the db with a client 56 | * Commands: 57 | * start - start the database 58 | * shell - start a bash shell ```dc run --rm postgres shell``` 59 | * backup - create a backup (```/data/backup/```) ```dc run --rm postgres backup``` 60 | * restore - restore from a backup (```/data/backup/```) ```dc run --rm postgres restore``` 61 | 2. django-python3 62 | * The projects can be found under the /src/ directory 63 | * Installed Apps: 64 | * Django: 1.10.1 65 | * uWSGI: 2.0.13.1 66 | * psycopg2: 2.6.2 67 | * django-debug-toolbar: 1.5 68 | * djangorestframework: 3.4.6 + optional packages 69 | * django-cleanup: 0.4.2 70 | * django-extensions: 1.7.4 71 | * django-compressor: 2.1 72 | * django-rosetta: 0.7.12 [optional] 73 | * django-modeltranslation: 0.11 [optional] 74 | * Commands: 75 | * shell -start a bash shell ```dc run --rm django shell``` 76 | * start_runserver - in development mode this will start django runserver 77 | * start_uwsgi - in production mode this will start the uwsgi 78 | 3. nginx 79 | * Commands: 80 | * shell -start a bash shell ```dc run --rm nginx shell``` 81 | * Installed Apps: 82 | * Nginx: 1.8.1 83 | 4. nodejs [optional] 84 | * Commands: 85 | * shell -start a bash shell ```dc run --rm nodejs shell``` 86 | * start_build - start the react build process than exit ```dc run --rm nodejs start_build``` 87 | * start_watchify - start the watchify process for the development mode 88 | * Installed Apps: 89 | * nodejs: 4.x.x 90 | * npm: 2.x.x 91 | * Installed Packages: [automatically installed] 92 | * react: 15.3.1, 93 | * react-dom: 15.3.1, 94 | * babelify: 7.3.0, 95 | * babel-preset-react: 6.11.1, 96 | * browserify: 13.1.0, 97 | * watchify: 3.7.0, 98 | * react-router: 2.8.0 99 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "_0": "\n\t Some information before the generation!\n", 3 | "_1": "\n\t project_name: The name of the project.", 4 | "_2": "\n\t repo_name: The directory name(automtically generated from the project_name).", 5 | "_3": "\n\t db_password: The postgres user password for the database.", 6 | "_4": "\n\t use_translation: Translation integration(Rosetta, ModelTransalation, admin integration)[True/False].", 7 | "_5": "\n\t use_react: React, NodeJS, React-Router to replace the django frontend.[True/False]", 8 | "_6": "\n\t admin_name: The name of the admin user who will receive the error messages.", 9 | "_7": "\n\t admin_email: The email address of the admin user who will receive the error messages.", 10 | "_8": "\n\t email_host_user: The gmail email address what the system can use to send emails.", 11 | "_9": "\n\t email_host_password: The gmail email address password what the system can use to send emails.\n", 12 | "_10": "\n\t Press ENTER to continue.\n", 13 | "*": "{{cookiecutter._0 + cookiecutter._1 + cookiecutter._2 + cookiecutter._3 + cookiecutter._4 + cookiecutter._5 + cookiecutter._6 + cookiecutter._7 + cookiecutter._8 + cookiecutter._9 + cookiecutter._10}}", 14 | "project_name": "project_name", 15 | "repo_name": "{{cookiecutter.project_name|replace(' ', '_')}}", 16 | "db_password": "db_password", 17 | "use_translation": "True", 18 | "use_react": "True", 19 | "admin_name": "", 20 | "admin_email": "", 21 | "email_host_user": "", 22 | "email_host_password": "" 23 | } 24 | -------------------------------------------------------------------------------- /hooks/post_gen_project.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | import os 4 | from random import SystemRandom 5 | import shutil 6 | 7 | 8 | def generate_secret_key(): 9 | """ 10 | Returns a securely generated random string. 11 | """ 12 | allowed_chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#%^&*(-_=+)' 13 | return ''.join(SystemRandom().choice(allowed_chars) for i in range(50)) 14 | 15 | 16 | def make_secret_key(project_directory): 17 | """Generates and saves random secret key""" 18 | # Determine the setting_file_location 19 | setting = os.path.join(project_directory, '.secret') 20 | 21 | with open(setting) as f: 22 | file_ = f.read() 23 | 24 | # Replace "CHANGEME!!!" with SECRET_KEY 25 | file_ = file_.replace('{SECRET_KEY}', generate_secret_key(), 1) 26 | 27 | # Write the results to the file 28 | with open(setting, 'w') as f: 29 | f.write(file_) 30 | 31 | 32 | # 1. Generates and saves random secret key 33 | PROJECT_DIR = os.path.realpath(os.path.curdir) 34 | make_secret_key(PROJECT_DIR) 35 | 36 | # 2. Rename the .src_gitignore file to .gitignore 37 | os.rename(PROJECT_DIR + '/.src_gitignore', PROJECT_DIR + '/.gitignore') 38 | 39 | {% if cookiecutter.use_translation != 'True' -%} 40 | # Remove the empty directory if we don't want to use rosetta 41 | shutil.rmtree(PROJECT_DIR + '/src/core/management/') 42 | shutil.rmtree(PROJECT_DIR + '/src/core/templates/admin/') 43 | shutil.rmtree(PROJECT_DIR + '/src/core/templates/rosetta/') 44 | {%- endif %} 45 | 46 | {% if cookiecutter.use_react != 'True' -%} 47 | # Remove the nodejs/rect files & directories 48 | shutil.rmtree(PROJECT_DIR + '/react/') 49 | shutil.rmtree(PROJECT_DIR + '/docker/nodejs/') 50 | shutil.rmtree(PROJECT_DIR + '/src/core/static/react/') 51 | os.remove(PROJECT_DIR + '/.eslintrc') 52 | {%- endif %} 53 | -------------------------------------------------------------------------------- /req-packages.txt: -------------------------------------------------------------------------------- 1 | cookiecutter==1.4.0 2 | click==6.6 3 | uwsgi==2.0.14 4 | django==1.10.2 5 | psycopg2==2.6.2 6 | django-debug-toolbar==1.6 7 | djangorestframework==3.4.7 8 | markdown==2.6.7 9 | django-filter==0.15.2 10 | django-crispy-forms==1.6.0 11 | django-cleanup==0.4.2 12 | django-extensions==1.7.4 13 | django-compressor==2.1 14 | django-modeltranslation==0.12 15 | django-rosetta==0.7.12 16 | -------------------------------------------------------------------------------- /req-test.txt: -------------------------------------------------------------------------------- 1 | cookiecutter==1.4.0 2 | pytest==3.0.3 3 | pytest-cookies==0.2.0 4 | pytest-cov==2.3.1 5 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = test*.py 3 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import pytest 4 | from cookiecutter.main import cookiecutter 5 | 6 | parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 7 | 8 | 9 | @pytest.fixture 10 | def del_gen_project(request): 11 | def teardown(): 12 | shutil.rmtree(os.path.join(parent_dir, 'project_name')) 13 | request.addfinalizer(teardown) 14 | 15 | 16 | def test_with_default_values(del_gen_project): 17 | ''' Successful generation with the default values ''' 18 | cookiecutter(parent_dir, no_input=True) 19 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | 4 | "env": { 5 | "browser": true, 6 | "node": true 7 | }, 8 | 9 | "ecmaFeatures": { 10 | "jsx": true 11 | }, 12 | 13 | "plugins": [ 14 | "react" 15 | ], 16 | 17 | "rules": { 18 | // 19 | //Possible Errors 20 | // 21 | "comma-dangle": 2, // disallow or enforce trailing commas 22 | "no-cond-assign": 2, // disallow assignment in conditional expressions 23 | "no-console": 0, // disallow use of console (off by default in the node environment) 24 | "no-constant-condition": 2, // disallow use of constant expressions in conditions 25 | "no-control-regex": 2, // disallow control characters in regular expressions 26 | "no-debugger": 2, // disallow use of debugger 27 | "no-dupe-args": 2, // disallow duplicate arguments in functions 28 | "no-dupe-keys": 2, // disallow duplicate keys when creating object literals 29 | "no-duplicate-case": 2, // disallow a duplicate case label. 30 | "no-empty": 2, // disallow empty statements 31 | "no-empty-character-class": 2, // disallow the use of empty character classes in regular expressions 32 | "no-ex-assign": 2, // disallow assigning to the exception in a catch block 33 | "no-extra-boolean-cast": 2, // disallow double-negation boolean casts in a boolean context 34 | "no-extra-parens": [0, "functions"], // disallow unnecessary parentheses (off by default) 35 | "no-extra-semi": 2, // disallow unnecessary semicolons 36 | "no-func-assign": 2, // disallow overwriting functions written as function declarations 37 | "no-inner-declarations": 2, // disallow function or variable declarations in nested blocks 38 | "no-invalid-regexp": 2, // disallow invalid regular expression strings in the RegExp constructor 39 | "no-irregular-whitespace": 2, // disallow irregular whitespace outside of strings and comments 40 | "no-negated-in-lhs": 2, // disallow negation of the left operand of an in expression 41 | "no-obj-calls": 2, // disallow the use of object properties of the global object (Math and JSON) as functions 42 | "no-regex-spaces": 2, // disallow multiple spaces in a regular expression literal 43 | "no-sparse-arrays": 2, // disallow sparse arrays 44 | "no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement 45 | "use-isnan": 2, // disallow comparisons with the value NaN 46 | "valid-jsdoc": 2, // Ensure JSDoc comments are valid (off by default) 47 | "valid-typeof": 2, // Ensure that the results of typeof are compared against a valid string 48 | // 49 | // Best Practices 50 | // 51 | "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default). 0: deep destructuring is not compatible https://github.com/eslint/eslint/issues/1863 52 | "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) 53 | "consistent-return": 0, // require return statements to either always or never specify values 54 | "curly": 2, // specify curly brace conventions for all control statements 55 | "default-case": 2, // require default case in switch statements (off by default) 56 | "dot-notation": 2, // encourages use of dot notation whenever possible 57 | "eqeqeq": 2, // require the use of === and !== 58 | "guard-for-in": 2, // make sure for-in loops have an if statement (off by default) 59 | "no-alert": 2, // disallow the use of alert, confirm, and prompt 60 | "no-caller": 2, // disallow use of arguments.caller or arguments.callee 61 | "no-div-regex": 2, // disallow division operators explicitly at beginning of regular expression (off by default) 62 | "no-else-return": 2, // disallow else after a return in an if (off by default) 63 | "no-eq-null": 2, // disallow comparisons to null without a type-checking operator (off by default) 64 | "no-eval": 2, // disallow use of eval() 65 | "no-extend-native": 2, // disallow adding to native types 66 | "no-extra-bind": 2, // disallow unnecessary function binding 67 | "no-fallthrough": 2, // disallow fallthrough of case statements 68 | "no-floating-decimal": 2, // disallow the use of leading or trailing decimal points in numeric literals (off by default) 69 | "no-implied-eval": 2, // disallow use of eval()-like methods 70 | "no-iterator": 2, // disallow usage of __iterator__ property 71 | "no-labels": 2, // disallow use of labeled statements 72 | "no-lone-blocks": 2, // disallow unnecessary nested blocks 73 | "no-loop-func": 2, // disallow creation of functions within loops 74 | "no-multi-spaces": 2, // disallow use of multiple spaces 75 | "no-multi-str": 2, // disallow use of multiline strings 76 | "no-native-reassign": 2, // disallow reassignments of native objects 77 | "no-new": 2, // disallow use of new operator when not part of the assignment or comparison 78 | "no-new-func": 2, // disallow use of new operator for Function object 79 | "no-new-wrappers": 2, // disallows creating new instances of String,Number, and Boolean 80 | "no-octal": 2, // disallow use of octal literals 81 | "no-octal-escape": 2, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; 82 | "no-param-reassign": 2, // disallow reassignment of function parameters (off by default) 83 | "no-process-env": 0, // disallow use of process.env (off by default) 84 | "no-proto": 2, // disallow usage of __proto__ property 85 | "no-redeclare": 2, // disallow declaring the same variable more then once 86 | "no-return-assign": 2, // disallow use of assignment in return statement 87 | "no-script-url": 0, // disallow use of javascript: urls. 88 | "no-self-compare": 2, // disallow comparisons where both sides are exactly the same (off by default) 89 | "no-sequences": 2, // disallow use of comma operator 90 | "no-throw-literal": 2, // restrict what can be thrown as an exception (off by default) 91 | "no-unused-expressions": 2, // disallow usage of expressions in statement position 92 | "no-void": 2, // disallow use of void operator (off by default) 93 | "no-warning-comments": [0, {"terms": ["todo", "fixme"], "location": "start"}], // disallow usage of configurable warning terms in comments": 2, // e.g. TODO or FIXME (off by default) 94 | "no-with": 2, // disallow use of the with statement 95 | "radix": 2, // require use of the second argument for parseInt() (off by default) 96 | "vars-on-top": 2, // requires to declare all vars on top of their containing scope (off by default) 97 | "wrap-iife": 2, // require immediate function invocation to be wrapped in parentheses (off by default) 98 | "yoda": 2, // require or disallow Yoda conditions 99 | // 100 | // Strict Mode 101 | // 102 | // "strict": 0, // controls location of Use Strict Directives. 0: required by `babel-eslint` 103 | // 104 | // Variables 105 | "no-catch-shadow": 2, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) 106 | "no-delete-var": 2, // disallow deletion of variables 107 | "no-label-var": 2, // disallow labels that share a name with a variable 108 | // "no-shadow": 2, // disallow declaration of variables already declared in the outer scope 109 | "no-shadow-restricted-names": 2, // disallow shadowing of names such as arguments 110 | "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block 111 | "no-undef-init": 2, // disallow use of undefined when initializing variables 112 | "no-undefined": 0, // disallow use of undefined variable (off by default) 113 | "no-unused-vars": 2, // disallow declaration of variables that are not used in the code 114 | "no-use-before-define": 2, // disallow use of variables before they are defined 115 | // 116 | //Stylistic Issues 117 | // 118 | "indent": [1, 2], // this option sets a specific tab width for your code (off by default) 119 | "brace-style": 1, // enforce one true brace style (off by default) 120 | "camelcase": [1, {"properties": "never"}], // require camel case names 121 | "comma-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after comma 122 | "comma-style": [1, "last"], // enforce one true comma style (off by default) 123 | "consistent-this": [1, "self"], // enforces consistent naming when capturing the current execution context (off by default) 124 | "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines 125 | "func-names": 0, // require function expressions to have a name (off by default) 126 | "func-style": 0, // enforces use of function declarations or expressions (off by default) 127 | "max-nested-callbacks": [1, 3], // specify the maximum depth callbacks can be nested (off by default) 128 | "new-cap": [1, {"newIsCap": true, "capIsNew": false}], // require a capital letter for constructors 129 | "new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments 130 | "newline-after-var": 0, // allow/disallow an empty newline after var statement (off by default) 131 | "no-array-constructor": 1, // disallow use of the Array constructor 132 | "no-inline-comments": 0, // disallow comments inline after code (off by default) 133 | "no-lonely-if": 1, // disallow if as the only statement in an else block (off by default) 134 | "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation 135 | "no-multiple-empty-lines": [1, {"max": 2}], // disallow multiple empty lines (off by default) 136 | "no-nested-ternary": 1, // disallow nested ternary expressions (off by default) 137 | "no-new-object": 1, // disallow use of the Object constructor 138 | "no-spaced-func": 1, // disallow space between function identifier and application 139 | "no-ternary": 0, // disallow the use of ternary operators (off by default) 140 | "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines 141 | "no-underscore-dangle": 1, // disallow dangling underscores in identifiers 142 | "one-var": [1,{ uninitialized: "always", initialized: "never" }], // allow just one var statement per function (off by default) 143 | "operator-assignment": [0, "never"], // require assignment operator shorthand where possible or prohibit it entirely (off by default) 144 | "padded-blocks": [1, "never"], // enforce padding within blocks (off by default) 145 | "quote-props": [1, "as-needed"], // require quotes around object literal property names (off by default) 146 | "quotes": [1, "single", "avoid-escape"], // specify whether double or single quotes should be used 147 | "semi": [1, "always"], // require or disallow use of semicolons instead of ASI 148 | "semi-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after semicolons 149 | "sort-vars": 0, // sort variables within the same declaration block (off by default) 150 | "key-spacing": [2, { 151 | "singleLine": { 152 | "mode": "strict" 153 | }, 154 | "multiLine": { 155 | "mode": "strict" 156 | } 157 | }], 158 | "space-before-blocks": [1, "always"], // require or disallow space before blocks (off by default) 159 | "space-before-function-paren": [1, {"anonymous": "always", "named": "never"}], // require or disallow space before function opening parenthesis (off by default) 160 | "space-in-parens": [1, "never"], // require or disallow spaces inside parentheses (off by default) 161 | "space-infix-ops": 1, // require spaces around operators 162 | "space-unary-ops": [1, {"words": true, "nonwords": false}], // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default) 163 | "spaced-comment": [1, "always"], // require or disallow a space immediately following the // in a line comment (off by default) 164 | "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) 165 | "object-curly-spacing": 1, 166 | "array-bracket-spacing": 1, 167 | // 168 | // ECMAScript 6 169 | // 170 | "no-var": 0, // require let or const instead of var (off by default) 171 | // "generator-star-spacing": [2, "before"], // enforce the spacing around the * in generator functions (off by default) 172 | // 173 | // Legacy 174 | // 175 | "max-depth": [2, 3], // specify the maximum depth that blocks can be nested (off by default) 176 | "max-len": [1, 99, 2], // specify the maximum length of a line in your program (off by default) 177 | "max-params": [2, 5], // limits the number of parameters that can be used in the function declaration. (off by default) 178 | "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) 179 | "no-bitwise": 0, // disallow use of bitwise operators (off by default) 180 | "no-plusplus": 2, // disallow use of unary operators, ++ and -- (off by default) 181 | // 182 | // eslint-plugin-react 183 | // 184 | // "react/jsx-uses-vars": 1 185 | "react/display-name": 0, // Prevent missing displayName in a React component definition 186 | "react/jsx-quotes": 0, // Enforce quote style for JSX attributes 187 | "react/jsx-no-undef": 2, // Disallow undeclared variables in JSX 188 | "react/jsx-sort-props": 0, // Enforce props alphabetical sorting 189 | "react/jsx-uses-react": 2, // Prevent React to be incorrectly marked as unused 190 | "react/jsx-uses-vars": 2, // Prevent variables used in JSX to be incorrectly marked as unused 191 | "react/no-did-mount-set-state": 2, // Prevent usage of setState in componentDidMount 192 | "react/no-did-update-set-state": 2, // Prevent usage of setState in componentDidUpdate 193 | "react/no-multi-comp": 0, // Prevent multiple component definition per file 194 | "react/no-unknown-property": 2, // Prevent usage of unknown DOM property 195 | "react/prop-types": 2, // Prevent missing props validation in a React component definition 196 | "react/react-in-jsx-scope": 2, // Prevent missing React when using JSX 197 | "react/self-closing-comp": 2, // Prevent extra closing tags for components without children 198 | "react/wrap-multilines": 2, // Prevent missing parentheses around multilines JSX 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.secret: -------------------------------------------------------------------------------- 1 | DJANGO_SECRET_KEY={SECRET_KEY} 2 | DB_PASSWORD={{cookiecutter.db_password}} 3 | ALLOWED_HOSTS={% if cookiecutter.use_translation == 'True' %} 4 | YANDEX_TRANSLATE_KEY= 5 | {%- endif %} 6 | EMAIL_HOST_USER={{cookiecutter.email_host_user}} 7 | EMAIL_HOST_PASSWORD={{cookiecutter.email_host_password}} 8 | STATIC_ROOT=/data/static/ 9 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.src_gitignore: -------------------------------------------------------------------------------- 1 | *.py[oc] 2 | __pycache__ 3 | {% if cookiecutter.use_react == 'True' -%} 4 | react/node_modules/* 5 | {%- endif %} 6 | env.txt 7 | container_shared/* 8 | !container_shared/.init 9 | {% if cookiecutter.use_react == 'True' -%} 10 | !src/core/static/react/.init 11 | src/core/static/react/* 12 | {%- endif -%} 13 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/buildall.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | echo "base" 3 | echo "--------------------" 4 | docker build \ 5 | --build-arg "DEVELOPER_UID=$(id -u)" \ 6 | --build-arg "DEVELOPER_GID=$(id -g)" \ 7 | -t {{cookiecutter.repo_name}}-base "docker/base" 8 | 9 | echo "postgres" 10 | echo "--------------------" 11 | docker build -t {{cookiecutter.repo_name}}-postgres "docker/postgres" 12 | 13 | echo "nginx" 14 | echo "--------------------" 15 | docker build -t {{cookiecutter.repo_name}}-nginx "docker/nginx" 16 | 17 | echo "django-python3" 18 | echo "--------------------" 19 | docker build \ 20 | -t {{cookiecutter.repo_name}}-django-python3 "docker/django-python3" 21 | 22 | {% if cookiecutter.use_react == 'True' -%} 23 | echo "nodejs" 24 | echo "--------------------" 25 | docker build -t {{cookiecutter.repo_name}}-nodejs "docker/nodejs" 26 | {%- endif %} 27 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/container_shared/.init: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker-cleanup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | echo "Docker image cleanup" 3 | echo "--------------------" 4 | docker rmi `docker images --filter 'dangling=true' -q --no-trunc` 5 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | volumes: 4 | {{cookiecutter.repo_name}}-data-volume: 5 | driver: local 6 | 7 | services: 8 | postgres: 9 | hostname: {{cookiecutter.repo_name}}-postgres 10 | image: {{cookiecutter.repo_name}}-postgres 11 | command: start 12 | volumes: 13 | - {{cookiecutter.repo_name}}-data-volume:/data 14 | - "./docker/postgres/config:/config" 15 | - "./container_shared:/container_shared" 16 | logging: 17 | options: 18 | max-size: "5m" 19 | max-file: "1" 20 | entrypoint: ['python3', '/config/run.py'] 21 | environment: 22 | PGDATA: /data/postgres 23 | env_file: .secret 24 | ports: 25 | - "5433:5432" 26 | 27 | django: 28 | hostname: {{cookiecutter.repo_name}}-django 29 | image: {{cookiecutter.repo_name}}-django-python3 30 | entrypoint: ['python3', '/config/run.py'] 31 | command: start_runserver 32 | user: developer 33 | volumes: 34 | - {{cookiecutter.repo_name}}-data-volume:/data 35 | - "./src:/src" 36 | - "./docker/django-python3/config:/config" 37 | - "./container_shared:/container_shared" 38 | links: 39 | - "postgres:postgres" 40 | logging: 41 | options: 42 | max-size: "5m" 43 | max-file: "1" 44 | environment: 45 | PYTHONPATH: /src 46 | DJANGO_SETTINGS_MODULE: core.settings 47 | PYTHONUNBUFFERED: "True" 48 | DEBUG: "True" 49 | env_file: .secret 50 | ports: 51 | - "80:8000" 52 | 53 | {% if cookiecutter.use_react == 'True' -%} 54 | nodejs: 55 | hostname: {{cookiecutter.repo_name}}-nodejs 56 | image: {{cookiecutter.repo_name}}-nodejs 57 | command: start_watchify 58 | volumes: 59 | - {{cookiecutter.repo_name}}-data-volume:/data 60 | - "./docker/nodejs/config:/config" 61 | - "./container_shared:/container_shared" 62 | - "./react:/react" 63 | - "./src:/src" 64 | logging: 65 | options: 66 | max-size: "5m" 67 | max-file: "1" 68 | entrypoint: ['python3', '/config/run.py'] 69 | env_file: .secret 70 | {% endif -%} 71 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | MAINTAINER Egyed Zoltán "zoltan.egyed@vertis.com" 3 | 4 | RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y upgrade 5 | ENV LANG C.UTF-8 6 | RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive \ 7 | apt-get install -y --no-install-recommends \ 8 | python3 \ 9 | ca-certificates \ 10 | python3-pip \ 11 | vim \ 12 | curl \ 13 | git 14 | 15 | RUN pip3 install click==6.6 16 | 17 | COPY skel /etc/skel 18 | RUN git clone https://github.com/legios89/molokai.git /etc/skel/.vim/ 19 | COPY skel/* /root/ 20 | COPY runutils.py /usr/local/lib/python3.4/dist-packages/runutils.py 21 | 22 | ARG DEVELOPER_UID 23 | ARG DEVELOPER_GID 24 | RUN groupadd -g $DEVELOPER_GID developer && \ 25 | useradd -u $DEVELOPER_UID -g $DEVELOPER_GID -m developer 26 | RUN groupadd -g 5432 postgres && useradd -u 5432 -g 5432 -m postgres 27 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/base/runutils.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | import os 4 | import sys 5 | import subprocess 6 | import signal 7 | import pwd 8 | import click 9 | import time 10 | 11 | 12 | def getvar(name, default=None, required=True): 13 | """ 14 | Returns the value of an environment variable. If the variable is not 15 | present, default will be used. If required is True, only not None values 16 | will be returned, and it will raise an exception instead of returning None. 17 | """ 18 | ret = os.environ.get(name, default) 19 | if required and ret is None: 20 | raise Exception('Environment variable %s is not set' % name) 21 | return ret 22 | 23 | 24 | def ensure_dir(dir, owner=None, group=None, permsission_str='777'): 25 | """Checks the existence of the given dir and creates it if not present.""" 26 | if not os.path.isdir(dir): 27 | os.makedirs(dir) 28 | 29 | if owner: 30 | subprocess.call(['chown', owner, dir]) 31 | if group: 32 | subprocess.call(['chgrp', group, dir]) 33 | if permsission_str: 34 | subprocess.call(['chmod', permsission_str, dir]) 35 | 36 | 37 | def run_cmd(args, message=None, user=None): 38 | """Executes a one-off command. The message will be printed on terminal.""" 39 | if message: 40 | click.echo(message + ' start ... ') 41 | 42 | _setuser = setuser(user) if user else None 43 | try: 44 | subprocess.check_output( 45 | args, stderr=subprocess.STDOUT, preexec_fn=_setuser) 46 | except subprocess.CalledProcessError as e: 47 | if message: 48 | click.secho(message + ' finish ✘', fg='red') 49 | for line in str(e.output).split('\\n'): 50 | click.secho(line, fg='red') 51 | raise 52 | else: 53 | if message: 54 | click.secho(message + ' finish ✔', fg='green') 55 | 56 | 57 | def run_daemon(params, signal_to_send=signal.SIGTERM, waitfunc=None, 58 | user=None, semafor=None, initfunc=None, exit_on_finish=True): 59 | """ 60 | Runs the command as the given user (or the caller by default) in daemon 61 | mode and exits with it's return code. Sends the specified signal to exit. 62 | If waitfunc is given it must accept an object and it should return as soon 63 | as possible if object.stopped evaluates to True. If semafor is provided it 64 | should be a path to a file. Before exit, this file should be deleted. 65 | If the exit_on_finish=False then the fucntion doesn't exit. 66 | """ 67 | class Stopper(object): 68 | def __init__(self): 69 | self.stopped = False 70 | 71 | class SubprocessWrapper(object): 72 | def __init__(self): 73 | self.subprocess = None 74 | 75 | subprocess_wrapper = SubprocessWrapper() 76 | stopper = Stopper() 77 | 78 | def cleanup(signum, frame): 79 | if subprocess_wrapper.subprocess: 80 | subprocess_wrapper.subprocess.send_signal(signal_to_send) 81 | stopper.stopped = True 82 | 83 | signal.signal(signal.SIGTERM, cleanup) 84 | 85 | if waitfunc: 86 | waitfunc(stopper) 87 | 88 | if initfunc: 89 | initfunc(stopper) 90 | 91 | _setuser = setuser(user) if user else None 92 | if not stopper.stopped: 93 | sp = subprocess.Popen(params, preexec_fn=_setuser) 94 | subprocess_wrapper.subprocess = sp 95 | 96 | if semafor: 97 | open(semafor, 'w').close() 98 | 99 | waitresult = sp.wait() 100 | else: 101 | waitresult = 0 102 | 103 | try: 104 | os.remove(semafor) 105 | except: 106 | pass 107 | 108 | if exit_on_finish: 109 | sys.exit(waitresult) 110 | 111 | 112 | def setuser(username): 113 | """ 114 | Returns a function that sets process uid, gid according to the given 115 | username. If the user does not exist, it raises an error. 116 | """ 117 | uid, gid, home = id(username) 118 | groups = list(set(os.getgrouplist(username, gid))) 119 | 120 | def chuser(): 121 | try: 122 | os.setgroups(groups) 123 | os.setgid(gid) 124 | os.setuid(uid) 125 | os.environ['HOME'] = home 126 | except: 127 | pass # Probably the container started as a not root user 128 | return chuser 129 | 130 | 131 | def id(username): 132 | """Returns uid, gid, home directory for the given username.""" 133 | userinfo = pwd.getpwnam(username) 134 | return userinfo.pw_uid, userinfo.pw_gid, userinfo.pw_dir 135 | 136 | 137 | def runbash(user): 138 | subprocess.call(['bash'], preexec_fn=setuser(user)) 139 | 140 | 141 | def sleep(): 142 | class Stopper(object): 143 | def __init__(self): 144 | self.stopped = False 145 | 146 | stopper = Stopper() 147 | 148 | def stop_sleep(signum, frame): 149 | stopper.stopped = True 150 | 151 | signal.signal(signal.SIGTERM, stop_sleep) 152 | while not stopper.stopped: 153 | time.sleep(1) 154 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/base/skel/.bashrc: -------------------------------------------------------------------------------- 1 | export PS1='\[\e[38;5;202m\]\u\[\e[0m\]@\[\e[38;5;86m\]\h\[\e[0m\]:\[\e[38;5;3m\]\w\[\e[0m\]$ ' 2 | alias python=python3 3 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/base/skel/.vimrc: -------------------------------------------------------------------------------- 1 | set nocompatible 2 | set autoindent 3 | set backup 4 | set nu 5 | set smartindent 6 | set showmatch 7 | set textwidth=80 8 | set title 9 | set tabstop=4 10 | set shiftwidth=4 11 | set softtabstop=4 12 | set expandtab 13 | syntax on 14 | set t_Co=256 15 | filetype on 16 | filetype indent on 17 | filetype plugin on 18 | set modeline 19 | set ls=2 20 | colorscheme molokai 21 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/django-python3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM {{cookiecutter.repo_name}}-base 2 | RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive \ 3 | apt-get install -y --no-install-recommends \ 4 | libpq5 \ 5 | libpython3.4 \ 6 | libpq-dev \ 7 | build-essential \ 8 | python3-dev 9 | 10 | RUN pip3 install \ 11 | uwsgi==2.0.14 \ 12 | django==1.10.2 \ 13 | psycopg2==2.6.2 \ 14 | django-debug-toolbar==1.6 \ 15 | djangorestframework==3.4.7 \ 16 | markdown==2.6.7 \ 17 | django-filter==0.15.2 \ 18 | django-crispy-forms==1.6.0 \ 19 | django-cleanup==0.4.2 \ 20 | django-extensions==1.7.4 \ 21 | django-compressor==2.1 22 | 23 | WORKDIR /src/ 24 | 25 | {% if cookiecutter.use_translation == 'True' -%} 26 | RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive \ 27 | apt-get install -y --no-install-recommends gettext 28 | RUN pip3 install \ 29 | django-modeltranslation==0.12 \ 30 | django-rosetta==0.7.12 31 | {%- endif %} 32 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/django-python3/config/run.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | import signal 4 | import time 5 | import click 6 | import psycopg2 7 | import os 8 | from django.conf import settings 9 | 10 | # Utils Imports 11 | from runutils import run_daemon, runbash, ensure_dir, getvar, run_cmd 12 | 13 | 14 | def waitfordb(stopper): 15 | """ Wait for the database to accept connections. """ 16 | tick = 0.1 17 | intervals = 100 * [10] 18 | for i in intervals: 19 | click.echo('checking connection ...') 20 | try: 21 | psycopg2.connect(host='postgres', port=5432, database="django", 22 | user="postgres", password=getvar('DB_PASSWORD')) 23 | except Exception as e: 24 | click.echo('could not connect yet') 25 | click.echo(e) 26 | else: 27 | return 28 | 29 | for w in range(i): 30 | if stopper.stopped: 31 | return 32 | time.sleep(tick) 33 | 34 | 35 | {%- if cookiecutter.use_translation == 'True' %} 36 | 37 | 38 | def generate_makemessages_command(domain): 39 | command = ['django-admin', 'makemessages', '-d', domain] 40 | 41 | for lang in settings.LANGUAGES: 42 | if lang[0] != settings.LANGUAGE_CODE: 43 | command.append('-l=' + lang[0]) 44 | return command 45 | {%- endif %} 46 | 47 | 48 | # INIT: WILL RUN BEFORE ANY COMMAND AND START # 49 | def init(stopper): 50 | ensure_dir('/data/logs/', owner='developer', group='developer') 51 | ensure_dir('/data/logs/django', owner='developer', group='developer') 52 | ensure_dir('/data/static', owner='developer', group='developer') 53 | {% if cookiecutter.use_translation == 'True' -%} 54 | ensure_dir('/src/locale', owner='developer', group='developer') 55 | {%- endif %} 56 | if not stopper.stopped: 57 | if settings.DEBUG is False: 58 | {%- if cookiecutter.use_react == 'True' %} 59 | cmd = ['django-admin', 'collectstatic', '--noinput', '-i', 'react'] 60 | {% else %} 61 | cmd = ['django-admin', 'collectstatic', '--noinput'] 62 | {% endif -%} 63 | run_cmd(cmd, user='developer') 64 | 65 | # Create db cache and other one time commands 66 | if os.path.isfile('/data/.init') is False: 67 | run_cmd(['django-admin', 'migrate'], user='developer') 68 | run_cmd(['django-admin', 'createcachetable', '-v', '0'], 69 | user='developer') 70 | 71 | with open("/data/.init", "a+") as f: 72 | f.write(''){% if cookiecutter.use_translation == 'True' %} 73 | 74 | run_cmd(generate_makemessages_command('django'), user='developer') 75 | run_cmd(generate_makemessages_command('djangojs'), user='developer'){% endif %} 76 | 77 | 78 | @click.group() 79 | def run(): 80 | pass 81 | 82 | 83 | @run.command() 84 | @click.argument('user', default='developer') 85 | def shell(user): 86 | runbash(user) 87 | 88 | 89 | @run.command() 90 | def start_runserver(): 91 | start = ['django-admin.py', 'runserver', '0.0.0.0:8000'] 92 | run_daemon(start, signal_to_send=signal.SIGINT, user='developer', 93 | waitfunc=waitfordb, initfunc=init) 94 | 95 | 96 | @run.command() 97 | def start_uwsgi(): 98 | """Starts the service.""" 99 | start = ["uwsgi", "--ini", '/config/uwsgi.conf', '--post-buffering', '1'] 100 | run_daemon(start, signal_to_send=signal.SIGQUIT, user='developer', 101 | waitfunc=waitfordb, initfunc=init) 102 | 103 | 104 | if __name__ == '__main__': 105 | run() 106 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/django-python3/config/uwsgi.conf: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | master = 1 3 | processes = 2 4 | threads = 1 5 | chdir = /src 6 | module = core.wsgi 7 | socket = /data/sock/uwsgi.sock 8 | chmod-socket = 666 9 | enable-threads = 1 10 | buffer-size = 65535 11 | max-requests=200 12 | harakiri=30 13 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM {{cookiecutter.repo_name}}-base 2 | 3 | RUN curl http://nginx.org/keys/nginx_signing.key | apt-key add - 4 | RUN echo "deb http://nginx.org/packages/debian/ jessie nginx" | tee -a /etc/apt/sources.list 5 | RUN echo "deb-src http://nginx.org/packages/debian/ jessie nginx" | tee -a /etc/apt/sources.list 6 | RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive \ 7 | apt-get install -y --no-install-recommends \ 8 | nginx 9 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/nginx/config/nginx.conf: -------------------------------------------------------------------------------- 1 | daemon off; 2 | worker_processes 2; 3 | user developer; 4 | 5 | error_log stderr info; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | http { 12 | port_in_redirect off; 13 | access_log off; 14 | error_log /data/logs/nginx/error.log error; 15 | 16 | include nginx_mime.types; 17 | default_type application/octet-stream; 18 | charset utf-8; 19 | sendfile on; 20 | tcp_nopush on; 21 | tcp_nodelay on; 22 | reset_timedout_connection on; 23 | client_max_body_size 10M; 24 | fastcgi_buffers 8 16k; 25 | fastcgi_buffer_size 32k; 26 | 27 | gzip on; 28 | gzip_comp_level 2; 29 | gzip_http_version 1.0; 30 | gzip_proxied any; 31 | gzip_min_length 126; 32 | gzip_buffers 16 8k; 33 | gzip_types *; 34 | 35 | upstream localhost { 36 | server django:8000; 37 | } 38 | 39 | server { 40 | listen 8080; 41 | 42 | location /static/ { 43 | alias /data/static/; 44 | } 45 | 46 | location /media/ { 47 | alias /data/media/; 48 | } 49 | 50 | location / { 51 | uwsgi_pass unix:///data/sock/uwsgi.sock; 52 | include /config/uwsgi_params; 53 | uwsgi_param Host $host; 54 | uwsgi_param X-Real-IP $remote_addr; 55 | uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for; 56 | uwsgi_param X-Forwarded-Proto https; 57 | uwsgi_request_buffering off; 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/nginx/config/nginx_mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/nginx/config/run.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | import click 4 | 5 | # Utils Imports 6 | from runutils import runbash, run_daemon, ensure_dir 7 | 8 | 9 | @click.group() 10 | def run(): 11 | ensure_dir('/data/logs/', owner='developer', group='developer') 12 | ensure_dir('/data/logs/nginx/', owner='developer', group='developer') 13 | 14 | 15 | @run.command() 16 | @click.argument('user', default='developer') 17 | def shell(user): 18 | runbash(user) 19 | 20 | 21 | @run.command() 22 | def start(): 23 | run_daemon(['nginx', '-c', '/config/nginx.conf']) 24 | 25 | 26 | if __name__ == '__main__': 27 | run() 28 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/nginx/config/uwsgi_params: -------------------------------------------------------------------------------- 1 | 2 | uwsgi_param QUERY_STRING $query_string; 3 | uwsgi_param REQUEST_METHOD $request_method; 4 | uwsgi_param CONTENT_TYPE $content_type; 5 | uwsgi_param CONTENT_LENGTH $content_length; 6 | 7 | uwsgi_param REQUEST_URI $request_uri; 8 | uwsgi_param PATH_INFO $document_uri; 9 | uwsgi_param DOCUMENT_ROOT $document_root; 10 | uwsgi_param SERVER_PROTOCOL $server_protocol; 11 | 12 | uwsgi_param REMOTE_ADDR $remote_addr; 13 | uwsgi_param REMOTE_PORT $remote_port; 14 | uwsgi_param SERVER_PORT $server_port; 15 | uwsgi_param SERVER_NAME $server_name; 16 | uwsgi_param HTTPS $https if_not_empty; -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/nodejs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM {{cookiecutter.repo_name}}-base 2 | RUN curl -sL https://deb.nodesource.com/setup_4.x | bash - 3 | RUN apt-get update && DEBIAN_FRONTEND=noninteractive \ 4 | apt-get install -y --force-yes --no-install-recommends nodejs 5 | 6 | WORKDIR /react/ 7 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/nodejs/config/run.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | import click 4 | 5 | # Utils Imports 6 | from runutils import runbash, run_cmd, getvar, ensure_dir, run_daemon 7 | 8 | 9 | @click.group() 10 | def run(): 11 | ensure_dir('/data/static', owner='developer', group='developer') 12 | ensure_dir('/data/static/react', owner='developer', group='developer') 13 | run_cmd(['npm', 'config', 'set', 'static_root', getvar('STATIC_ROOT')], 14 | user='developer') 15 | run_daemon(['npm', 'install'], user='developer', exit_on_finish=False) 16 | 17 | 18 | @run.command() 19 | @click.argument('user', default='developer') 20 | def shell(user): 21 | runbash(user) 22 | 23 | 24 | @run.command() 25 | def start_watchify(): 26 | run_daemon(['npm', 'run', 'watch'], user='developer') 27 | 28 | 29 | @run.command() 30 | def start_build(): 31 | run_cmd(['npm', 'config', 'set', 'NODE_ENV', 'production'], 32 | user='developer') 33 | run_cmd(['npm', 'run', 'build'], message="npm run build", user='developer') 34 | 35 | 36 | if __name__ == '__main__': 37 | run() 38 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM {{cookiecutter.repo_name}}-base 2 | RUN set -x \ 3 | && apt-get update \ 4 | && DEBIAN_FRONTEND=noninteractive \ 5 | apt-get install -y --no-install-recommends \ 6 | postgresql-common \ 7 | && sed -ri 's/#(create_main_cluster) .*$/\1 = false/' \ 8 | /etc/postgresql-common/createcluster.conf \ 9 | && DEBIAN_FRONTEND=noninteractive \ 10 | apt-get install -y --no-install-recommends \ 11 | postgresql-9.4 \ 12 | postgresql-contrib-9.4 13 | 14 | ENV PATH /usr/lib/postgresql/9.4/bin:$PATH 15 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/postgres/config/pg_hba.conf: -------------------------------------------------------------------------------- 1 | local all all trust 2 | host all all ::1/0 md5 3 | host all all 0.0.0.0/0 md5 4 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/postgres/config/postgresql.conf: -------------------------------------------------------------------------------- 1 | hba_file = '/config/pg_hba.conf' 2 | listen_addresses = '*' 3 | port = 5432 4 | max_connections = 1000 5 | unix_socket_directories = '/data/sock/' 6 | ssl = false 7 | shared_buffers = 128MB 8 | log_destination = 'stderr' 9 | logging_collector = on 10 | log_directory = '/data/logs/postgres' 11 | log_filename = 'postgresql-%Y-%m-%d.log' 12 | log_file_mode = 0600 13 | log_rotation_age = 1d 14 | client_min_messages = notice 15 | log_min_messages = warning 16 | log_min_error_statement = error 17 | log_min_duration_statement = 2000 18 | log_connections = on 19 | log_disconnections = on 20 | log_duration = off 21 | log_line_prefix = '|%m|%u|' 22 | log_statement = 'ddl' 23 | log_timezone = 'UTC' 24 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docker/postgres/config/run.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | import os 4 | import re 5 | import sys 6 | import subprocess 7 | import time 8 | import signal 9 | from contextlib import contextmanager 10 | import click 11 | 12 | # Utils Imports 13 | from runutils import ( 14 | run_daemon, getvar, runbash, id, run_cmd, setuser, ensure_dir) 15 | 16 | 17 | PGDATA = getvar('PGDATA') 18 | CONFIG_FILE = '/config/postgresql.conf' 19 | SOCKET_DIR = '/data/sock' 20 | BACKUP_DIR = '/data/backup' 21 | SEMAFOR = '/data/sock/pg_semafor' 22 | start_postgres = ['postgres', '-c', 'config_file=%s' % CONFIG_FILE] 23 | 24 | 25 | ############# 26 | # Functions # 27 | ############# 28 | 29 | def psqlparams(command=None, database='django'): 30 | """Returns a list of command line arguments to run psql.""" 31 | if command is None: 32 | return ['psql', '-d', database, '-h', SOCKET_DIR] 33 | return ['psql', '-d', database, '-h', SOCKET_DIR, '-c', command] 34 | 35 | 36 | @contextmanager 37 | def running_db(): 38 | """ 39 | Starts and stops postgres (if it is not running) so the block 40 | inside the with statement can execute command against it. 41 | """ 42 | 43 | subproc = None 44 | if not os.path.isfile(os.path.join(PGDATA, 'postmaster.pid')): 45 | setpostgresuser = setuser('postgres') 46 | subproc = subprocess.Popen( 47 | start_postgres, 48 | preexec_fn=setpostgresuser, 49 | stdout=subprocess.PIPE, 50 | stderr=subprocess.PIPE) 51 | 52 | click.echo('Waiting for database to start...') 53 | time.sleep(1) 54 | 55 | try: 56 | yield 57 | finally: 58 | if subproc: 59 | subproc.send_signal(signal.SIGTERM) 60 | click.echo('Waiting for database to stop...') 61 | subproc.wait() 62 | 63 | 64 | def _initdb(): 65 | """Initialize the database.""" 66 | run_cmd(['initdb'], user='postgres', message='Initializing the database') 67 | _createdb('django', 'postgres') 68 | 69 | 70 | def _setpwd(username, password): 71 | """Sets the password for the given user.""" 72 | sql = "ALTER USER %s WITH PASSWORD '%s'" % (username, password) 73 | with running_db(): 74 | run_cmd(psqlparams(sql), 'Setting password', user='postgres') 75 | 76 | 77 | def _createdb(dbname, owner): 78 | """Creates a database.""" 79 | sql = "CREATE DATABASE %s WITH ENCODING 'UTF8' OWNER %s" % (dbname, owner) 80 | with running_db(): 81 | run_cmd(psqlparams(sql, database='postgres'), 'Creating database', 82 | user='postgres') 83 | 84 | 85 | def _backup(backupname): 86 | """Backs up the database with pg_dump.""" 87 | # We have some restrictions on the backupname 88 | if re.match('[a-z0-9_-]+$', backupname) is None: 89 | click.secho('Invalid backupname.', fg='red') 90 | sys.exit(1) 91 | 92 | # The file must not exist 93 | filename = os.path.join(BACKUP_DIR, backupname) 94 | if os.path.isfile(filename): 95 | click.secho('File %s exists.' % filename, fg='red') 96 | sys.exit(1) 97 | 98 | params = ['pg_dump', '-h', SOCKET_DIR, '-O', '-x', '-U', 'postgres', 99 | 'django'] 100 | with open(filename, 'w') as f, running_db(): 101 | ret = subprocess.call(params, stdout=f, preexec_fn=setuser('postgres')) 102 | 103 | uid, gid, _ = id('postgres') 104 | os.chown(filename, uid, gid) 105 | 106 | if ret == 0: 107 | click.secho('Successful backup: %s' % filename, fg='green') 108 | else: 109 | try: 110 | os.remove(filename) 111 | except: 112 | pass 113 | click.secho('Backup (%s) failed' % filename, fg='red') 114 | sys.exit(1) 115 | 116 | 117 | def _restore(backupname): 118 | filename = os.path.join(BACKUP_DIR, backupname) 119 | if not os.path.isfile(filename): 120 | click.secho('File %s does not exist.' % filename, fg='red') 121 | sys.exit(1) 122 | 123 | with running_db(): 124 | run_cmd(['psql', '-h', SOCKET_DIR, '-c', 'DROP DATABASE django'], 125 | message='Dropping database django', user='postgres') 126 | 127 | _createdb('django', 'postgres') 128 | run_cmd(psqlparams() + ['-f', filename], message='Restoring', 129 | user='postgres') 130 | 131 | 132 | ################################################ 133 | # INIT: WILL RUN BEFORE ANY COMMAND AND START # 134 | ################################################ 135 | def init(stopper=None): 136 | ensure_dir('/data/logs/', owner='developer', group='developer') 137 | ensure_dir(os.path.split(PGDATA)[0], owner='postgres', group='postgres') 138 | ensure_dir(SOCKET_DIR, owner='postgres', group='postgres') 139 | ensure_dir(BACKUP_DIR, owner='postgres', group='postgres') 140 | ensure_dir('/data/logs/postgres', owner='postgres', group='postgres') 141 | 142 | if not os.path.isdir(PGDATA): 143 | _initdb() 144 | _setpwd('postgres', getvar('DB_PASSWORD')) 145 | 146 | 147 | ############# 148 | # COMMANDS # 149 | ############# 150 | @click.group() 151 | def run(): 152 | pass 153 | 154 | 155 | @run.command() 156 | @click.argument('user', default='postgres') 157 | def shell(user): 158 | init() 159 | runbash(user) 160 | 161 | 162 | @run.command() 163 | @click.option('--backupname', prompt=True) 164 | def restore(backupname): 165 | _restore(backupname) 166 | 167 | 168 | @run.command() 169 | @click.option('--backupname', prompt=True) 170 | def backup(backupname): 171 | _backup(backupname) 172 | 173 | 174 | @run.command() 175 | def start(): 176 | run_daemon(start_postgres, user='postgres', semafor=SEMAFOR, initfunc=init) 177 | 178 | 179 | if __name__ == '__main__': 180 | run() 181 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/production-docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | volumes: 4 | {{cookiecutter.repo_name}}-data-volume: 5 | driver: local 6 | 7 | services: 8 | postgres: 9 | hostname: {{cookiecutter.repo_name}}-postgres 10 | image: {{cookiecutter.repo_name}}-postgres 11 | command: start 12 | volumes: 13 | - {{cookiecutter.repo_name}}-data-volume:/data 14 | - "./container_shared:/container_shared" 15 | - "./docker/postgres/config:/config" 16 | logging: 17 | options: 18 | max-size: "5m" 19 | max-file: "1" 20 | entrypoint: ['python3', '/config/run.py'] 21 | environment: 22 | PGDATA: /data/postgres 23 | env_file: .secret 24 | ports: 25 | - "5433:5432" 26 | 27 | django: 28 | hostname: {{cookiecutter.repo_name}}-django 29 | image: {{cookiecutter.repo_name}}-django-python3 30 | entrypoint: ['python3', '/config/run.py'] 31 | command: start_uwsgi 32 | user: developer 33 | volumes: 34 | - {{cookiecutter.repo_name}}-data-volume:/data 35 | - "./src:/src" 36 | - "./docker/django-python3/config:/config" 37 | - "./container_shared:/container_shared" 38 | links: 39 | - "postgres:postgres" 40 | logging: 41 | options: 42 | max-size: "5m" 43 | max-file: "1" 44 | environment: 45 | PYTHONPATH: /src 46 | DJANGO_SETTINGS_MODULE: core.settings 47 | PYTHONUNBUFFERED: "True" 48 | DEBUG: "False" 49 | env_file: .secret 50 | 51 | nginx: 52 | hostname: {{cookiecutter.repo_name}}-nginx 53 | image: {{cookiecutter.repo_name}}-nginx 54 | entrypoint: ['python3', '/config/run.py'] 55 | command: start 56 | volumes: 57 | - {{cookiecutter.repo_name}}-data-volume:/data 58 | - "./container_shared:/container_shared" 59 | - "./docker/nginx/config:/config" 60 | links: 61 | - "django:django" 62 | logging: 63 | options: 64 | max-size: "5m" 65 | max-file: "1" 66 | ports: 67 | - "80:8080" 68 | 69 | {% if cookiecutter.use_react == 'True' -%} 70 | nodejs: 71 | hostname: {{cookiecutter.repo_name}}-nodejs 72 | image: {{cookiecutter.repo_name}}-nodejs 73 | command: start_build 74 | volumes: 75 | - {{cookiecutter.repo_name}}-data-volume:/data 76 | - "./docker/nodejs/config:/config" 77 | - "./container_shared:/container_shared" 78 | - "./react:/react" 79 | - "./src:/src" 80 | logging: 81 | options: 82 | max-size: "5m" 83 | max-file: "1" 84 | entrypoint: ['python3', '/config/run.py'] 85 | env_file: .secret 86 | {% endif -%} 87 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/react/components/app.jsx: -------------------------------------------------------------------------------- 1 | /* global gettext */ 2 | var React = require('react'); 3 | var request = require('request-json'); 4 | 5 | var App = React.createClass({ 6 | propTypes: { 7 | children: React.PropTypes.any, 8 | route: React.PropTypes.object 9 | }, 10 | 11 | getInitialState: function () { 12 | return {urls: {}}; 13 | }, 14 | 15 | componentWillMount: function () { 16 | var self = this; 17 | var url = {% if cookiecutter.use_translation == 'True' %}'/' + this.props.route.language + {% endif %}'/api/urls/'; 18 | var client = request.createClient(window.location.origin + '/'); 19 | client.get(url, function (err, res, body) { 20 | self.setState({urls: body}); 21 | }); 22 | }, 23 | 24 | render: function () { 25 | return ( 26 |
27 |
28 |
29 |
30 |

{gettext('Home')}

31 | 39 |
40 |
41 |
42 | {React.cloneElement(this.props.children, {urls: this.state.urls})} 43 |
44 |
45 |
); 46 | } 47 | }); 48 | module.exports = App; 49 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/react/components/home.jsx: -------------------------------------------------------------------------------- 1 | /* global gettext */ 2 | var React = require('react'); 3 | 4 | var Home = React.createClass({ 5 | propTypes: { 6 | urls: React.PropTypes.object 7 | }, 8 | 9 | render: function () { 10 | return ( 11 |
12 |

{gettext('It worked!')}

13 |

14 | {gettext('Congratulations on your first Django-powered page.')} 15 |

16 |
17 | ); 18 | } 19 | }); 20 | module.exports = Home; 21 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/react/components/router.jsx: -------------------------------------------------------------------------------- 1 | /* global gettext */ 2 | var React = require('react'); 3 | var ReactDOM = require('react-dom'); 4 | var ReactRouter = require('react-router'); 5 | var Router = ReactRouter.Router; 6 | var Route = ReactRouter.Route; 7 | var useRouterHistory = ReactRouter.useRouterHistory; 8 | var createHashHistory = require('react-router/node_modules/history/lib/createHashHistory'); 9 | 10 | // Components 11 | var App = require('./app.jsx'); 12 | var Home = require('./home.jsx'); 13 | 14 | 15 | var NotFoundRoute = React.createClass({ 16 | render: function () { 17 | return (
{gettext('NOT FOUND')}
); 18 | } 19 | }); 20 | 21 | var appHistory = useRouterHistory(createHashHistory)({queryKey: false}); 22 | var mainContainer = document.getElementById('app-place'); 23 | var language = mainContainer.attributes['data-language'].value; 24 | 25 | ReactDOM.render(( 26 | 27 | 28 | 29 | 30 | 31 | ), mainContainer); 32 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "main": "router.jsx", 4 | "scripts": { 5 | "build": "browserify -t [ babelify --presets [ react ] ] components/router.jsx -o $npm_config_static_root/react/app.js", 6 | "watch": "watchify components/router.jsx -v -o /src/core/static/react/app.js -t [ babelify --presets [ react ] ] --debug" 7 | }, 8 | "dependencies": { 9 | "react": "15.3.1", 10 | "react-dom": "15.3.1", 11 | "babelify": "7.3.0", 12 | "babel-preset-react": "6.11.1", 13 | "browserify": "13.1.0", 14 | "watchify": "3.7.0", 15 | "react-router": "2.8.0", 16 | "request": "2.75.0", 17 | "request-json": "0.6.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/legios89/django-docker-bootstrap/9ceff89e5ced60d16c02ed4040712bfe39093f16/{{cookiecutter.repo_name}}/src/core/__init__.py -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/legios89/django-docker-bootstrap/9ceff89e5ced60d16c02ed4040712bfe39093f16/{{cookiecutter.repo_name}}/src/core/management/commands/__init__.py -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/management/commands/makemessages.py: -------------------------------------------------------------------------------- 1 | from django.core.management.commands import makemessages 2 | import shutil 3 | import os 4 | 5 | 6 | class Command(makemessages.Command): 7 | def folder_to_folder(self, root_src_dir, root_dst_dir, method): 8 | for src_dir, dirs, files in os.walk(root_src_dir): 9 | dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1) 10 | if not os.path.exists(dst_dir): 11 | os.makedirs(dst_dir) 12 | for file_ in files: 13 | src_file = os.path.join(src_dir, file_) 14 | dst_file = os.path.join(dst_dir, file_) 15 | if os.path.exists(dst_file): 16 | os.remove(dst_file) 17 | 18 | if method == 'move': 19 | shutil.move(src_file, dst_dir) 20 | else: 21 | shutil.copy(src_file, dst_dir) 22 | if method == 'move': 23 | shutil.rmtree(root_src_dir) 24 | 25 | def handle(self, *args, **options): 26 | root_src_dir = '/src/locale/' 27 | root_dst_dir = '/data/locale/' 28 | 29 | self.folder_to_folder(root_dst_dir, root_src_dir, 'copy') 30 | super(Command, self).handle(*args, **options) 31 | self.folder_to_folder(root_src_dir, root_dst_dir, 'move') 32 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/settings.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | import os 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | def getvar(name, default=None, required=True): 8 | """ 9 | Returns the value of an environment variable. 10 | If the variable is not present, default will be used. 11 | If required is True, only not None values will be returned, 12 | will raise an exception instead of returning None. 13 | """ 14 | ret = os.environ.get(name, default) 15 | if required and ret is None: 16 | raise Exception('Environment variable %s is not set' % name) 17 | return ret 18 | 19 | 20 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 21 | SECRET_KEY = getvar('DJANGO_SECRET_KEY') 22 | DEBUG = getvar('DEBUG', 'True') == 'True' 23 | ALLOWED_HOSTS = getvar('ALLOWED_HOSTS', '').split(',') 24 | LOGIN_URL = '/admin/login/' 25 | LOGIN_REDIRECT_URL = '/admin/' 26 | 27 | # Application definition 28 | INSTALLED_APPS = ( 29 | # project apps 30 | 'core', 31 | {%- if cookiecutter.use_translation == 'True' %} 32 | 'modeltranslation', 33 | {%- endif %} 34 | # django core 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | # 3rd party 42 | 'debug_toolbar', 43 | 'rest_framework', 44 | 'django_cleanup', 45 | 'django_extensions', 46 | 'compressor', 47 | {%- if cookiecutter.use_translation == 'True' %} 48 | 'rosetta', 49 | {%- endif %} 50 | ) 51 | 52 | MIDDLEWARE_CLASSES = ( 53 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 54 | 'django.contrib.sessions.middleware.SessionMiddleware', 55 | {% if cookiecutter.use_translation == 'True' -%} 56 | 'django.middleware.locale.LocaleMiddleware', 57 | {% endif -%} 58 | 'django.middleware.common.CommonMiddleware', 59 | 'django.middleware.csrf.CsrfViewMiddleware', 60 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 61 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 62 | 'django.contrib.messages.middleware.MessageMiddleware', 63 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 64 | 'django.middleware.security.SecurityMiddleware', 65 | ) 66 | 67 | ROOT_URLCONF = 'core.urls' 68 | 69 | TEMPLATES = [ 70 | { 71 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 72 | 'DIRS': [], 73 | 'OPTIONS': { 74 | 'context_processors': [ 75 | 'django.template.context_processors.debug', 76 | 'django.template.context_processors.request', 77 | 'django.contrib.auth.context_processors.auth', 78 | 'django.contrib.messages.context_processors.messages', 79 | ], 80 | 'loaders': [ 81 | 'django.template.loaders.filesystem.Loader', 82 | 'django.template.loaders.app_directories.Loader', 83 | ], 84 | }, 85 | }, 86 | ] 87 | 88 | WSGI_APPLICATION = 'core.wsgi.application' 89 | 90 | DATABASES = { 91 | 'default': { 92 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 93 | 'HOST': 'postgres', 94 | 'PORT': '5432', 95 | 'NAME': 'django', 96 | 'USER': 'postgres', 97 | 'PASSWORD': getvar('DB_PASSWORD') 98 | } 99 | } 100 | 101 | CACHES = { 102 | 'default': { 103 | 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 104 | 'LOCATION': 'cache_table', 105 | } 106 | } 107 | 108 | LANGUAGE_CODE = 'en' 109 | LANGUAGES = [('en', _('English'))] 110 | {% if cookiecutter.use_translation == 'True' -%} 111 | LOCALE_PATHS = ('/data/locale/',) 112 | if getvar('YANDEX_TRANSLATE_KEY', required=False): 113 | ROSETTA_ENABLE_TRANSLATION_SUGGESTIONS = True 114 | YANDEX_TRANSLATE_KEY = getvar('YANDEX_TRANSLATE_KEY') 115 | {%- endif %} 116 | 117 | TIME_ZONE = 'UTC' 118 | USE_I18N = True 119 | USE_L10N = True 120 | USE_TZ = True 121 | STATIC_URL = '/static/' 122 | STATIC_ROOT = getvar('STATIC_ROOT') 123 | MEDIA_ROOT = '/data/media/' 124 | MEDIA_URL = '/media/' 125 | 126 | STATICFILES_FINDERS = ( 127 | 'django.contrib.staticfiles.finders.FileSystemFinder', 128 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 129 | 'compressor.finders.CompressorFinder' 130 | ) 131 | 132 | # Debug toolbar visibility fix 133 | DEBUG_TOOLBAR_CONFIG = { 134 | 'SHOW_TOOLBAR_CALLBACK': lambda x: DEBUG, 135 | } 136 | 137 | LOGGING = { 138 | 'version': 1, 139 | 'disable_existing_loggers': False, 140 | 'formatters': { 141 | 'simple': { 142 | 'format': '%(levelname)s %(asctime)s %(module)s %(message)s' 143 | } 144 | }, 145 | 'filters': { 146 | 'require_debug_true': { 147 | '()': 'django.utils.log.RequireDebugTrue', 148 | }, 149 | 'require_debug_false': { 150 | '()': 'django.utils.log.RequireDebugFalse', 151 | } 152 | }, 153 | 'handlers': { 154 | 'console': { 155 | 'level': 'INFO', 156 | 'filters': ['require_debug_true'], 157 | 'class': 'logging.StreamHandler', 158 | 'formatter': 'simple' 159 | }, 160 | 'mail_admins': { 161 | 'level': 'ERROR', 162 | 'filters': ['require_debug_false'], 163 | 'class': 'django.utils.log.AdminEmailHandler' 164 | }, 165 | 'django_file': { 166 | 'level': 'INFO', 167 | 'class': 'logging.handlers.TimedRotatingFileHandler', 168 | 'filename': '/data/logs/django/django.log', 169 | 'when': 'midnight', 170 | 'backupCount': 30, 171 | 'formatter': 'simple', 172 | }, 173 | }, 174 | 'loggers': { 175 | 'django': { 176 | 'handlers': ['console', 'django_file'], 177 | 'propagate': True, 178 | }, 179 | 'django.request': { 180 | 'handlers': ['mail_admins'], 181 | 'level': 'ERROR', 182 | 'propagate': True, 183 | }, 184 | 'django.template': { 185 | 'handlers': ['mail_admins'], 186 | 'level': 'ERROR', 187 | 'propagate': True, 188 | }, 189 | } 190 | } 191 | 192 | ADMINS = [('{{cookiecutter.admin_name}}', '{{cookiecutter.admin_email}}'), ] 193 | EMAIL_SUBJECT_PREFIX = '[Django {{cookiecutter.project_name}}] ' 194 | EMAIL_USE_TLS = True 195 | EMAIL_HOST = 'smtp.gmail.com' 196 | EMAIL_PORT = 587 197 | EMAIL_HOST_USER = getvar('EMAIL_HOST_USER') 198 | EMAIL_HOST_PASSWORD = getvar('EMAIL_HOST_PASSWORD') 199 | SERVER_EMAIL = getvar('EMAIL_HOST_USER') 200 | DEFAULT_FROM_EMAIL = getvar('EMAIL_HOST_USER') 201 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/static/css/cover.css: -------------------------------------------------------------------------------- 1 | /* Links */ 2 | a, 3 | a:focus, 4 | a:hover { 5 | color: #fff; 6 | } 7 | 8 | /* Custom default button */ 9 | .btn-default, 10 | .btn-default:hover, 11 | .btn-default:focus { 12 | color: #333; 13 | text-shadow: none; /* Prevent inheritence from `body` */ 14 | background-color: #fff; 15 | border: 1px solid #fff; 16 | } 17 | 18 | /* Base structure */ 19 | html, 20 | body { 21 | height: 100%; 22 | background-color: #333; 23 | } 24 | body { 25 | color: #fff; 26 | text-align: center; 27 | text-shadow: 0 1px 3px rgba(0,0,0,.5); 28 | } 29 | 30 | /* Extra markup and styles for table-esque vertical and horizontal centering */ 31 | .site-wrapper { 32 | display: table; 33 | width: 100%; 34 | height: 100%; /* For at least Firefox */ 35 | min-height: 100%; 36 | -webkit-box-shadow: inset 0 0 100px rgba(0,0,0,.5); 37 | box-shadow: inset 0 0 100px rgba(0,0,0,.5); 38 | } 39 | .site-wrapper-inner { 40 | display: table-cell; 41 | vertical-align: top; 42 | } 43 | .cover-container { 44 | margin-right: auto; 45 | margin-left: auto; 46 | } 47 | 48 | /* Padding for spacing */ 49 | .inner { 50 | padding: 30px; 51 | } 52 | 53 | /* Header */ 54 | .masthead-brand { 55 | margin-top: 10px; 56 | margin-bottom: 10px; 57 | } 58 | 59 | .masthead-nav > li { 60 | display: inline-block; 61 | } 62 | .masthead-nav > li + li { 63 | margin-left: 20px; 64 | } 65 | .masthead-nav > li > a { 66 | padding-right: 0; 67 | padding-left: 0; 68 | font-size: 16px; 69 | font-weight: bold; 70 | color: #fff; /* IE8 proofing */ 71 | color: rgba(255,255,255,.75); 72 | border-bottom: 2px solid transparent; 73 | } 74 | .masthead-nav > li > a:hover, 75 | .masthead-nav > li > a:focus { 76 | background-color: transparent; 77 | border-bottom-color: #a9a9a9; 78 | border-bottom-color: rgba(255,255,255,.25); 79 | } 80 | .masthead-nav > .active > a, 81 | .masthead-nav > .active > a:hover, 82 | .masthead-nav > .active > a:focus { 83 | color: #fff; 84 | border-bottom-color: #fff; 85 | } 86 | 87 | @media (min-width: 768px) { 88 | .masthead-brand { 89 | float: left; 90 | } 91 | .masthead-nav { 92 | float: right; 93 | } 94 | } 95 | 96 | /* Cover */ 97 | .cover { 98 | padding: 0 20px; 99 | } 100 | .cover .btn-lg { 101 | padding: 10px 20px; 102 | font-weight: bold; 103 | } 104 | 105 | /* Footer */ 106 | .mastfoot { 107 | color: #999; /* IE8 proofing */ 108 | color: rgba(255,255,255,.5); 109 | } 110 | 111 | /* Affix and center */ 112 | @media (min-width: 768px) { 113 | /* Pull out the header and footer */ 114 | .masthead { 115 | position: fixed; 116 | top: 0; 117 | } 118 | .mastfoot { 119 | position: fixed; 120 | bottom: 0; 121 | } 122 | /* Start the vertical centering */ 123 | .site-wrapper-inner { 124 | vertical-align: middle; 125 | } 126 | /* Handle the widths */ 127 | .masthead, 128 | .mastfoot, 129 | .cover-container { 130 | width: 100%; /* Must be percentage or pixels for horizontal alignment */ 131 | } 132 | } 133 | 134 | @media (min-width: 992px) { 135 | .masthead, 136 | .mastfoot, 137 | .cover-container { 138 | width: 700px; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/static/react/.init: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/legios89/django-docker-bootstrap/9ceff89e5ced60d16c02ed4040712bfe39093f16/{{cookiecutter.repo_name}}/src/core/static/react/.init -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/templates/admin/base_site.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "admin/base_site.html" %} 2 | {% load i18n %} 3 | {% block welcome-msg %} 4 | {{block.super}} 5 | {% trans 'Rosetta' %} / 6 | {% endblock %} 7 | {% endraw %} 8 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/templates/home.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% load staticfiles %} 2 | {% load compress %} 3 | {% load i18n %} 4 | {% get_current_language as LANGUAGE_CODE %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% trans "Home Page" %} 12 | {% compress css %} 13 | 14 | {% endcompress %} 15 | 16 | {% endraw %}{% if cookiecutter.use_react == 'True' %} 17 |
18 |
{%- else -%}{% raw %} 19 |
20 |
21 |
22 |
23 |
24 |

{% trans "Home" %}

25 | 33 |
34 |
35 |
36 |

{% trans "It worked!" %}

37 |

38 | {% trans "Congratulations on your first Django-powered page." %} 39 |

40 |
41 |
42 |
43 |
{% endraw %}{% endif %} 44 | 45 | {% if cookiecutter.use_react == 'True' -%}{% raw %} 46 | {% compress js %} 47 | 48 | {% endcompress %} 49 | {% endraw %}{% endif -%} 50 | 51 | 52 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/templates/rosetta/base.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "rosetta/base.html" %} 2 | {% load i18n %} 3 | 4 | {% block header %} 5 |
6 |

Rosetta

7 |
8 |
9 | {% block welcome-msg %} 10 | {% trans 'Welcome,' %} 11 | {% firstof user.get_short_name user.get_username %}. 12 | {% trans 'Admin Site' %} / 13 | {% endblock %} 14 | {% block userlinks %} 15 | {% if site_url %} 16 | {% trans 'View site' %} / 17 | {% endif %} 18 | {% if user.is_active and user.is_staff %} 19 | {% url 'django-admindocs-docroot' as docsroot %} 20 | {% if docsroot %} 21 | {% trans 'Documentation' %} / 22 | {% endif %} 23 | {% endif %} 24 | {% if user.has_usable_password %} 25 | 26 | {% trans 'Change password' %} 27 | / 28 | {% endif %} 29 | {% trans 'Log out' %} 30 | {% endblock %} 31 |
32 | {% endblock %} 33 | {% endraw %} 34 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/templates/rosetta/languages.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "rosetta/languages.html" %} 2 | 3 | {% block main %} 4 | {{ block.super }} 5 | 14 | {% endblock %}{% endraw %} 15 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/templates/rosetta/pofile.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "rosetta/pofile.html" %} 2 | {% load i18n %} 3 | 4 | {% block header %} 5 | 12 | {% endblock %} 13 | {% endraw %} 14 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/urls.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | from django.conf.urls import include, url 4 | from django.contrib import admin 5 | from django.conf import settings 6 | from django.conf.urls.static import static 7 | from django.views.i18n import javascript_catalog 8 | {% if cookiecutter.use_translation == 'True' -%} 9 | from django.conf.urls.i18n import i18n_patterns 10 | {% endif %} 11 | # Project imports 12 | from .views import HomePageView{% if cookiecutter.use_translation == 'True' %}, PublishRosetta{% endif %}{% if cookiecutter.use_react == 'True' %}, UrlsApi{% endif %} 13 | 14 | {% if cookiecutter.use_translation == 'True' -%} 15 | urlpatterns = i18n_patterns( 16 | {%- else -%} 17 | urlpatterns = [ 18 | {%- endif %} 19 | url(r'^$', HomePageView.as_view(), name='home'), 20 | url(r'^admin/', include(admin.site.urls)), 21 | url(r'^api-auth/', include('rest_framework.urls', 22 | namespace='rest_framework')), 23 | url(r'^jsi18n/$', javascript_catalog, name='javascript-catalog'), 24 | {%- if cookiecutter.use_react == 'True' %} 25 | url(r'^api/urls/$', UrlsApi.as_view(), name='api_urls'), 26 | {%- endif %} 27 | {%- if cookiecutter.use_translation == 'True' %} 28 | url(r'^rosetta/', include('rosetta.urls')), 29 | url(r'^publish/rosetta/', PublishRosetta.as_view(), name='publish_rosetta') 30 | ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 31 | {% else %} 32 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 33 | {% endif %} 34 | if settings.DEBUG: 35 | import debug_toolbar 36 | urlpatterns.append(url(r'^__debug__/', include(debug_toolbar.urls))) 37 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/views.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Core and 3th party packages 3 | {% if cookiecutter.use_react == 'True' -%} 4 | from rest_framework.views import APIView 5 | from rest_framework.response import Response 6 | {% if cookiecutter.use_translation != 'True' -%} 7 | from django.core.urlresolvers import reverse 8 | {% endif -%} 9 | {% endif -%} 10 | {% if cookiecutter.use_translation == 'True' -%} 11 | from django.views.generic import View, TemplateView 12 | from django.http import HttpResponseRedirect 13 | from django.core.urlresolvers import reverse 14 | 15 | 16 | class PublishRosetta(View): 17 | def get(self, request): 18 | try: 19 | import uwsgi 20 | uwsgi.reload() 21 | except ImportError: 22 | pass # Probably the django started with runserver 23 | return HttpResponseRedirect(reverse('rosetta-home')) 24 | {% else -%} 25 | from django.views.generic import TemplateView 26 | {% endif %} 27 | 28 | class HomePageView(TemplateView): 29 | template_name = "home.html" 30 | {% if cookiecutter.use_react == 'True' %} 31 | 32 | class UrlsApi(APIView): 33 | def get(self, request, format=None): 34 | return Response({'admin_index': reverse('admin:index')}) 35 | {%- endif %} 36 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/core/wsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.core.wsgi import get_wsgi_application 3 | 4 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 5 | application = get_wsgi_application() 6 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 7 | from django.core.management import execute_from_command_line 8 | execute_from_command_line(sys.argv) 9 | --------------------------------------------------------------------------------