├── .dockerignore
├── .editorconfig
├── .eslintrc
├── .gitignore
├── Makefile
├── README.md
├── django_apps
├── __init__.py
└── templates
│ ├── base.html
│ └── index.html
├── django_config
├── __init__.py
├── settings
│ ├── __init__.py
│ ├── base.py
│ ├── local.py
│ └── production.py
├── urls.py
└── wsgi.py
├── docker-compose-development.yml
├── docker-compose.yml
├── docker_compose
├── django
│ ├── development
│ │ ├── Dockerfile
│ │ └── start.sh
│ ├── entrypoint.sh
│ └── production
│ │ ├── Dockerfile
│ │ └── start.sh
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
├── node
│ └── development
│ │ ├── Dockerfile
│ │ └── start.sh
└── postgres
│ └── Dockerfile
├── manage.py
├── package.json
├── requirements
├── base.txt
├── local.txt
└── production.txt
├── static
└── js
│ └── src
│ ├── example.spec.js
│ ├── main
│ ├── components
│ │ └── hello-world.jsx
│ ├── containers
│ │ ├── DevTools
│ │ │ └── index.jsx
│ │ └── Root
│ │ │ ├── Root.dev.jsx
│ │ │ ├── Root.prod.jsx
│ │ │ └── index.jsx
│ ├── index.jsx
│ ├── reducers
│ │ └── index.jsx
│ ├── routes.jsx
│ └── store
│ │ ├── configureStore.dev.jsx
│ │ ├── configureStore.prod.jsx
│ │ └── index.jsx
│ ├── test_index.js
│ └── utilities
│ ├── api.js
│ └── cookie.js
└── webpack
├── karma.config.js
├── webpack.base.config.js
├── webpack.local.config.js
└── webpack.production.config.js
/.dockerignore:
--------------------------------------------------------------------------------
1 | # virtual environments
2 | venv
3 |
4 | # Python files
5 | *.pyc
6 | __pycache__
7 |
8 | # Python Notebook dot files
9 | .ipynb*
10 |
11 | # Secret environment settings
12 | .env
13 | env
14 |
15 | # Node and Webpack files
16 | node_modules
17 | npm-debug.log
18 | webpack-stats*
19 |
20 | # Static assests
21 | staticfiles
22 | static_cdn
23 | media_cdn
24 |
25 | # Ignore sqlite
26 | db.sqlite3
27 |
28 | # Apple files
29 | .DS_Store
30 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | # Unix-style newlines with a newline ending every file
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | # Python settings
11 | [*.py]
12 | line_length=120
13 | indent_style = space
14 | indent_size = 4
15 |
16 | # JavaScript, JSX, CSS, SCSS, HTML settings
17 | [*.{js,jsx,css,scss,html}]
18 | indent_style = space
19 | indent_size = 2
20 |
21 | # Markdown settings
22 | [*.md]
23 | trim_trailing_whitespace = false
24 |
25 | # Config files
26 | [*.conf]
27 | indent_style = space
28 | indent_size = 2
29 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "ecmaFeatures": {
4 | "jsx": true,
5 | "modules": true
6 | },
7 | "env": {
8 | "browser": true,
9 | "es6": true,
10 | "node": true
11 | },
12 | "parser": "babel-eslint",
13 | "rules": {
14 | "react/jsx-uses-react": 2,
15 | "react/jsx-uses-vars": 2,
16 | "react/react-in-jsx-scope": 2,
17 | "no-var": 0,
18 | "vars-on-top": 0,
19 | "comma-dangle": 0,
20 | "arrow-body-style": ["error", "always"],
21 | "import/no-extraneous-dependencies": ["error", {"devDependencies": true}],
22 | "max-len": ["error", { "comments": 120, "code": 120 }],
23 | "no-console": ["error", { "allow": ["warn", "error"] }],
24 | "no-unused-expressions": ["error", { "allowShortCircuit": true }],
25 | "arrow-body-style": ["error", "as-needed"]
26 | },
27 | "plugins": [
28 | "react"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # virtual environments
2 | venv
3 |
4 | # Python files
5 | *.pyc
6 | __pycache__
7 |
8 | # Python Notebook dot files
9 | .ipynb*
10 |
11 | # Secret environment settings
12 | .env
13 | env
14 |
15 | # Node and Webpack files
16 | node_modules
17 | npm-debug.log
18 | webpack-stats*
19 |
20 | # Static assests
21 | staticfiles
22 | static_cdn
23 | media_cdn
24 |
25 | # Ignore sqlite
26 | db.sqlite3
27 |
28 | # Apple files
29 | .DS_Store
30 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | docker-build:
2 | docker-compose -f docker-compose.yml build
3 |
4 | docker-build-dev:
5 | docker-compose -f docker-compose-development.yml build
6 |
7 | docker-up-background:
8 | docker-compose -f docker-compose.yml up -d
9 |
10 | docker-up-background-dev:
11 | docker-compose -f docker-compose-development.yml up -d
12 |
13 | docker-up:
14 | docker-compose -f docker-compose.yml up
15 |
16 | docker-up-dev:
17 | docker-compose -f docker-compose-development.yml up
18 |
19 | ssh-django:
20 | docker exec -ti django_server bash
21 |
22 | ssh-node:
23 | docker exec -ti node_server bash
24 |
25 | ssh-psql:
26 | docker exec -ti psql_server bash
27 |
28 | ssh-nginx:
29 | docker exec -ti nginx_server bash
30 |
31 | django-dev:
32 | python manage.py runserver --settings=django_config.settings.local
33 |
34 | # docker-clean-up:
35 | # docker rm -f $(docker ps -a -q) -v
36 | # docker rmi -f $(docker images -a -q)
37 | # docker volume rm $(docker volume ls -q)
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **NOTE: THIS HAS BEEN DEPRECATED. Use [this](https://github.com/chopdgd/cookiecutter-django-reactjs) instead. Integrates Facebooks create react app with Django :)**
2 |
3 | # An example project for a Dockerized Django/ReactJS web application
4 |
5 | I set this up because it was __SUCH__ a pain in the a** to set up a web application with all of the latest technologies. I created this as a boilerplate after much research and reading of blogs. For a detailed step by step for creating this project look [here](https://gist.github.com/genomics-geek/98929a9e7ba9602fed7bfa4a5a1c5c4e/). Or you can just clone [this repo](https://github.com/genomics-geek/django_reactjs_boilerplate) and follow the [local deployment instructions](https://github.com/genomics-geek/django_reactjs_boilerplate#setup-local-environment).
6 |
7 | This is an example of a project that uses the following technologies:
8 | + Docker with Docker Compose
9 | + Django REST APIs
10 | + ReactJS with Redux
11 | + Webpack module bundler and Hot Reloading (Uses React Hot Reloader 3!)
12 | + Karma with Mocha for JavaScript unit tests
13 |
14 | ## System Requirements
15 |
16 | + [Docker](https://www.docker.com/)
17 | + [Docker Compose](https://docs.docker.com/compose/)
18 | + [PostgreSQL](http://www.postgresql.org/)
19 | + [pyenv](https://github.com/yyuu/pyenv)
20 | + [pyenv-virtualenv](https://github.com/yyuu/pyenv-virtualenv)
21 | + [git](https://git-scm.com/)
22 | + [NodeJS 6+](https://nodejs.org/en/)
23 | + [NPM 3+](https://www.npmjs.com/)
24 |
25 | ## Setup Local Environment
26 |
27 | ### First things, first. Clone the repo
28 |
29 | ```
30 | git clone https://github.com/genomics-geek/django_reactjs_boilerplate.git
31 | ```
32 |
33 | ### To setup a Dockerized development environment
34 |
35 | In my opinion, this is the easiest way to get up and running.
36 |
37 | #### 1. Create `.env` file
38 |
39 | [Example .env file](https://gist.github.com/genomics-geek/98929a9e7ba9602fed7bfa4a5a1c5c4e#create-environment-variables-file---env)
40 |
41 | #### 2. Build docker container
42 |
43 | ```
44 | make docker-build-dev
45 | make docker-up-dev
46 | ```
47 |
48 | Visit [http://localhost:8000](http://localhost:8000). You should see __Hello World!__. You should now see the application running locally! Also this project is set up to use hot reloading, so you can see your changes to React components live ;)
49 |
50 | ### To setup a local development environment
51 |
52 | If you want to a non dockerized development environment
53 |
54 | #### 1. Set up a virtual environment - [Quick Guide to Virtual Environments](https://gist.github.com/genomics-geek/98929a9e7ba9602fed7bfa4a5a1c5c4e#file-step05-md)
55 |
56 | ```
57 | pyenv virtualenv [name_of_venv]
58 | ```
59 |
60 | #### 2. Install requirements
61 |
62 | ```
63 | npm install
64 | pip install -r requirements/local.txt
65 | ```
66 |
67 | #### 3. Create .env file
68 |
69 | [Example .env file](https://gist.github.com/genomics-geek/98929a9e7ba9602fed7bfa4a5a1c5c4e#create-environment-variables-file---env)
70 |
71 | #### 4. Create the PostgreSQL database
72 |
73 | [Quick Guide for installing PostgreSQL on MacOSX](https://gist.github.com/genomics-geek/98929a9e7ba9602fed7bfa4a5a1c5c4e#set-up-postgresql-database)
74 |
75 | #### 5. Start the node server
76 |
77 | Open a terminal window and run:
78 |
79 | ```
80 | npm run watch
81 | ```
82 |
83 | #### 7. Start the django server
84 |
85 | Before you run python manage.py run:
86 |
87 | ```
88 | export DJANGO_SETTINGS_MODULE=django_config.settings.local
89 | ```
90 |
91 | Or alternatively, you can just add `--settings=django_config.settings.local` to any `python manage.py` command you run.
92 |
93 | Open another terminal window and run:
94 |
95 | ```
96 | python manage.py migrate
97 | python manage.py runserver
98 | ```
99 |
100 | Visit [http://localhost:8000](http://localhost:8000). You should see __Hello World!__. You should now see the application running locally! Also this project is set up to use hot reloading, so you can see your changes to React components live ;)
101 |
102 | ## Languages used
103 | + [Python](https://www.python.org/)
104 | + [JavaScript](https://www.javascript.com/)
105 | + [JSX](https://jsx.github.io/)
106 |
107 | ## Tools used
108 | + [Docker 1.12.1](https://www.docker.com/)
109 | + [Docker compose 1.8.0](https://docs.docker.com/compose/)
110 | + [Django 1.10.2](https://www.djangoproject.com/)
111 | + [Django REST Framework 3.4.7](http://www.django-rest-framework.org/)
112 | + [ReactJS 15.3.2](https://facebook.github.io/react/)
113 | + [Redux 3.6.0](http://redux.js.org/docs/introduction/)
114 | + [PostgreSQL 9.6.0](http://www.postgresql.org/)
115 | + [NodeJS 6.7.0](https://nodejs.org/en/)
116 | + [NPM 3.10.7](https://www.npmjs.com/)
117 | + [Webpack 1.13.2](https://webpack.github.io/)
118 |
119 | ## Helpful resources
120 | + [Gist on setting up Django + ReactJS project](https://gist.github.com/genomics-geek/49ae04fb3d0147bc8340edf228759c36)
121 | + [Learn Redux from the Creator!](https://egghead.io/courses/getting-started-with-redux)
122 | + [More from Dan](https://egghead.io/courses/building-react-applications-with-idiomatic-redux)
123 | + [Dockerizing Django projects](http://ruddra.com/2016/08/14/docker-django-nginx-postgres/)
124 | + [Owais Lane Blog Post on Django + ReactJS with Webpack](http://owaislone.org/blog/webpack-plus-reactjs-and-django/)
125 | + [Extension of Owais Lane's blog post](http://geezhawk.github.io/using-react-with-django-rest-framework)
126 | + [YouTube Playlist on Django Rest Framework](https://www.youtube.com/playlist?list=PLEsfXFp6DpzTOcOVdZF-th7BS_GYGguAS)
127 | + [YouTube Playlist on ReactJS/Redux](https://www.youtube.com/playlist?list=PLoYCgNOIyGABj2GQSlDRjgvXtqfDxKm5b)
128 | + [fullstackpython](http://www.fullstackpython.com/)
129 | + [Write your first Django App](https://docs.djangoproject.com/en/1.10/intro/tutorial01/)
130 | + [Quick Django tutorial](http://drksephy.github.io/2015/07/16/django/)
131 | + [Django REST framework](http://www.django-rest-framework.org/tutorial/1-serialization/)
132 | + [Using React with Django, with a little help from Webpack](http://geezhawk.github.io/using-react-with-django-rest-framework)
133 | + [Hot Module Replacement with Webpack](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html)
134 | + [Example of React Hot Reloader 3](https://github.com/gaearon/redux-devtools/tree/master/examples/todomvc)
135 |
--------------------------------------------------------------------------------
/django_apps/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/genomics-geek/django_reactjs_boilerplate/7668038f182c1600324a2c62c29f490f8203c9dc/django_apps/__init__.py
--------------------------------------------------------------------------------
/django_apps/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 | {% block head %}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {% block head_title %}{% endblock head_title %}
17 |
18 |
19 | {% block stylesheets %}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {% endblock stylesheets %}
36 |
37 |
38 |
39 |
40 | {% endblock head %}
41 |
42 |
43 |
44 |
45 |
46 | {% block body %}
47 | {% endblock body %}
48 |
49 |
50 |
51 | {% block javascript %}
52 | {% endblock javascript %}
53 |
54 |
55 | {% block google_analytics %}
56 | {% endblock google_analytics %}
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/django_apps/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load staticfiles %}
3 | {% load render_bundle from webpack_loader %}
4 |
5 | {% block body %}
6 |
7 | {% endblock body %}
8 |
9 | {% block javascript %}
10 | {% render_bundle 'main' %}
11 | {% endblock javascript %}
12 |
--------------------------------------------------------------------------------
/django_config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/genomics-geek/django_reactjs_boilerplate/7668038f182c1600324a2c62c29f490f8203c9dc/django_config/__init__.py
--------------------------------------------------------------------------------
/django_config/settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/genomics-geek/django_reactjs_boilerplate/7668038f182c1600324a2c62c29f490f8203c9dc/django_config/settings/__init__.py
--------------------------------------------------------------------------------
/django_config/settings/base.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for django_config project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.10.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.10/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.10/ref/settings/
11 | """
12 |
13 | import dj_database_url
14 | import os
15 |
16 | from decouple import config
17 |
18 |
19 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
20 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
21 | APPS_DIR = os.path.join(BASE_DIR, 'django_apps')
22 |
23 |
24 | # SECRET KEY
25 | # ------------------------------------------------------------------------------
26 | # https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-SECRET_KEY
27 | # SECURITY WARNING: keep the secret key used in production secret!
28 |
29 | SECRET_KEY = config('SECRET_KEY')
30 |
31 |
32 | # DEBUG
33 | # ------------------------------------------------------------------------------
34 | # https://docs.djangoproject.com/en/1.10/ref/settings/#debug
35 | # SECURITY WARNING: don't run with debug turned on in production!
36 |
37 | DEBUG = config('DEBUG', cast=bool)
38 |
39 |
40 | # MANAGER CONFIGURATION
41 | # ------------------------------------------------------------------------------
42 | # https://docs.djangoproject.com/en/1.10/ref/settings/#admins
43 | # https://docs.djangoproject.com/en/1.10/ref/settings/#managers
44 |
45 | ADMINS = (
46 | ("""Michael A. Gonzalez""", 'GonzalezMA.CHOP@gmail.com'),
47 | )
48 |
49 | MANAGERS = ADMINS
50 |
51 |
52 | # APP CONFIGURATION
53 | # ------------------------------------------------------------------------------
54 | # https://docs.djangoproject.com/en/1.10/ref/settings/#installed-apps
55 |
56 | DJANGO_APPS = [
57 | 'django.contrib.admin',
58 | 'django.contrib.auth',
59 | 'django.contrib.contenttypes',
60 | 'django.contrib.sessions',
61 | 'django.contrib.sites',
62 | 'django.contrib.messages',
63 | 'django.contrib.staticfiles',
64 | ]
65 |
66 | THIRD_PARTY_APPS = [
67 | 'allauth',
68 | 'allauth.account',
69 | 'rest_framework',
70 | 'rest_framework.authtoken',
71 | 'rest_auth',
72 | 'webpack_loader',
73 | ]
74 |
75 | LOCAL_APPS = []
76 |
77 | INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
78 |
79 |
80 | # MIDDLEWARE CONFIGURATION
81 | # ------------------------------------------------------------------------------
82 | # https://docs.djangoproject.com/en/1.10/topics/http/middleware/
83 |
84 | DJANGO_SECURITY_MIDDLEWARE = [
85 | 'django.middleware.security.SecurityMiddleware',
86 | ]
87 |
88 | DJANGO_MIDDLEWARE = [
89 | 'django.contrib.sessions.middleware.SessionMiddleware',
90 | 'django.middleware.common.CommonMiddleware',
91 | 'django.middleware.csrf.CsrfViewMiddleware',
92 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
93 | 'django.contrib.messages.middleware.MessageMiddleware',
94 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
95 | ]
96 |
97 | MIDDLEWARE = DJANGO_SECURITY_MIDDLEWARE + DJANGO_MIDDLEWARE
98 |
99 |
100 | # URL Configuration
101 | # ------------------------------------------------------------------------------
102 | # https://docs.djangoproject.com/en/1.10/ref/settings/#root-urlconf
103 |
104 | ROOT_URLCONF = 'django_config.urls'
105 |
106 |
107 | # TEMPLATE CONFIGURATION
108 | # ------------------------------------------------------------------------------
109 | # https://docs.djangoproject.com/en/1.10/topics/templates/
110 |
111 | TEMPLATES = [
112 | {
113 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
114 | 'DIRS': [os.path.join(APPS_DIR, 'templates')],
115 | 'APP_DIRS': True,
116 | 'OPTIONS': {
117 | 'context_processors': [
118 | 'django.template.context_processors.debug',
119 | 'django.template.context_processors.request',
120 | 'django.contrib.auth.context_processors.auth',
121 | 'django.contrib.messages.context_processors.messages',
122 | ],
123 | },
124 | },
125 | ]
126 |
127 |
128 | # WGSI CONFIGURATION
129 | # ------------------------------------------------------------------------------
130 | # https://docs.djangoproject.com/en/1.10/ref/settings/#wsgi-application
131 |
132 | WSGI_APPLICATION = 'django_config.wsgi.application'
133 |
134 |
135 | # DATABASE CONFIGURATION
136 | # ------------------------------------------------------------------------------
137 | # See: https://docs.djangoproject.com/en/1.10/ref/settings/#databases
138 |
139 | DATABASES = {
140 | 'default': dj_database_url.parse(config('DATABASE_URL')),
141 | }
142 | DATABASES['default']['ATOMIC_REQUESTS'] = True
143 |
144 | # Added this to support deployment on Heroku
145 | # https://devcenter.heroku.com/articles/django-app-configuration
146 | db_from_env = dj_database_url.config(conn_max_age=500)
147 | DATABASES['default'].update(db_from_env)
148 |
149 |
150 | # PASSWORD VALIDATION
151 | # ------------------------------------------------------------------------------
152 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
153 |
154 | AUTH_PASSWORD_VALIDATORS = [
155 | {
156 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
157 | },
158 | {
159 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
160 | },
161 | {
162 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
163 | },
164 | {
165 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
166 | },
167 | ]
168 |
169 |
170 | # GENERAL CONFIGURATION
171 | # ------------------------------------------------------------------------------
172 | # Local time zone for this installation. Choices can be found here:
173 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
174 | # although not all choices may be available on all operating systems.
175 | # In a Windows environment this must be set to your system time zone.
176 | # https://docs.djangoproject.com/en/1.10/topics/i18n/
177 |
178 | LANGUAGE_CODE = 'en-us'
179 |
180 | TIME_ZONE = 'UTC'
181 |
182 | USE_I18N = True
183 |
184 | USE_L10N = True
185 |
186 | USE_TZ = True
187 |
188 | SITE_ID = 1
189 |
190 |
191 | # STATIC FILE CONFIGURATION
192 | # ------------------------------------------------------------------------------
193 | # https://docs.djangoproject.com/en/1.10/howto/static-files/
194 |
195 | STATIC_URL = '/static/'
196 |
197 | STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
198 |
199 | STATIC_ROOT = os.path.join(BASE_DIR, 'static_cdn')
200 |
201 |
202 | # MEDIA CONFIGURATION
203 | # ------------------------------------------------------------------------------
204 | # https://docs.djangoproject.com/en/1.10/ref/settings/#media-root
205 | # https://docs.djangoproject.com/en/1.10/ref/settings/#media-url
206 |
207 | MEDIA_URL = "/media/"
208 |
209 | MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media_cdn")
210 |
211 |
212 | # Django REST framework
213 | # ------------------------------------------------------------------------------
214 | # http://www.django-rest-framework.org/
215 |
216 | REST_FRAMEWORK = {
217 | 'DEFAULT_PERMISSION_CLASSES': (
218 | 'rest_framework.permissions.IsAuthenticated',
219 | ),
220 | 'DEFAULT_AUTHENTICATION_CLASSES': (
221 | 'rest_framework.authentication.SessionAuthentication',
222 | 'rest_framework.authentication.BasicAuthentication',
223 | 'rest_framework.authentication.TokenAuthentication',
224 | 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
225 | ),
226 | }
227 |
228 |
229 | # Django REST-AUTH framework
230 | # ------------------------------------------------------------------------------
231 | # https://github.com/Tivix/django-rest-auth/
232 |
233 | REST_USE_JWT = True
234 |
235 |
236 | # EMAIL CONFIGURATION
237 | # ------------------------------------------------------------------------------
238 | # https://docs.djangoproject.com/en/1.10/topics/email/
239 |
240 | # EMAIL CONFIGURATION
241 | # ------------------------------------------------------------------------------
242 | # https://docs.djangoproject.com/en/1.10/topics/email/
243 |
244 | EMAIL_PORT = config('EMAIL_PORT')
245 |
246 | EMAIL_HOST = config('EMAIL_HOST')
247 |
248 | EMAIL_BACKEND = config('EMAIL_BACKEND')
249 |
250 | DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL')
251 |
--------------------------------------------------------------------------------
/django_config/settings/local.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | from .base import *
4 |
5 |
6 | # Webpack Loader by Owais Lane
7 | # ------------------------------------------------------------------------------
8 | # https://github.com/owais/django-webpack-loader
9 |
10 | WEBPACK_LOADER = {
11 | 'DEFAULT': {
12 | 'BUNDLE_DIR_NAME': 'js/builds-dev/',
13 | 'STATS_FILE': os.path.join(BASE_DIR, 'webpack', 'webpack-stats.dev.json')
14 | }
15 | }
16 |
17 | # Django Debug Toolbar
18 | # ------------------------------------------------------------------------------
19 | # https://github.com/jazzband/django-debug-toolbar
20 |
21 | MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
22 |
23 | INSTALLED_APPS += ('debug_toolbar', )
24 |
25 | INTERNAL_IPS = ['127.0.0.1', '10.0.2.2', ]
26 |
27 | # Hack to have debug toolbar when developing with docker
28 | ip = socket.gethostbyname(socket.gethostname())
29 | INTERNAL_IPS += [ip[:-1] + "1"]
30 |
--------------------------------------------------------------------------------
/django_config/settings/production.py:
--------------------------------------------------------------------------------
1 | from .base import *
2 |
3 |
4 | # Webpack Loader by Owais Lane
5 | # ------------------------------------------------------------------------------
6 | # https://github.com/owais/django-webpack-loader
7 |
8 | WEBPACK_LOADER = {
9 | 'DEFAULT': {
10 | 'BUNDLE_DIR_NAME': 'js/builds/',
11 | 'STATS_FILE': os.path.join(BASE_DIR, 'webpack', 'webpack-stats.production.json')
12 | }
13 | }
14 |
15 |
16 | # Use Whitenoise to serve static files
17 | # ------------------------------------------------------------------------------
18 | # https://whitenoise.readthedocs.io/
19 |
20 | MIDDLEWARE = DJANGO_SECURITY_MIDDLEWARE + ['whitenoise.middleware.WhiteNoiseMiddleware'] + DJANGO_MIDDLEWARE
21 |
22 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
23 |
24 |
25 | # Use Gunicorn as WSGI HTTP server
26 | # ------------------------------------------------------------------------------
27 | # http://gunicorn.org/
28 |
29 | INSTALLED_APPS += ('gunicorn', )
30 |
31 |
32 | # SITE CONFIGURATION
33 | # ------------------------------------------------------------------------------
34 | # Hosts/domain names that are valid for this site
35 | # See https://docs.djangoproject.com/en/1.6/ref/settings/#allowed-hosts
36 |
37 | ALLOWED_HOSTS = config(
38 | 'ALLOWED_HOSTS',
39 | cast=lambda v: [d for d in [s.strip() for s in v.split(' ')] if d],
40 | default='',
41 | )
42 |
43 |
44 | # EMAIL CONFIGURATION - Anymail with Mailgun
45 | # ------------------------------------------------------------------------------
46 | # https://docs.djangoproject.com/en/1.10/topics/email/
47 | # https://github.com/anymail/django-anymail
48 |
49 | INSTALLED_APPS += ('anymail', )
50 |
51 | ANYMAIL = {
52 | 'MAILGUN_API_KEY': config('MAILGUN_API_KEY'),
53 | }
54 |
55 |
56 | # LOGGING CONFIGURATION
57 | # ------------------------------------------------------------------------------
58 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
59 | # A sample logging configuration. The only tangible logging
60 | # performed by this configuration is to send an email to
61 | # the site admins on every HTTP 500 error when DEBUG=False.
62 | # See http://docs.djangoproject.com/en/dev/topics/logging for
63 | # more details on how to customize your logging configuration.
64 |
65 | LOGGING = {
66 | 'version': 1,
67 | 'disable_existing_loggers': False,
68 | 'filters': {
69 | 'require_debug_false': {
70 | '()': 'django.utils.log.RequireDebugFalse'
71 | }
72 | },
73 | 'formatters': {
74 | 'verbose': {
75 | 'format': '%(levelname)s %(asctime)s %(module)s '
76 | '%(process)d %(thread)d %(message)s'
77 | },
78 | },
79 | 'handlers': {
80 | 'mail_admins': {
81 | 'level': 'ERROR',
82 | 'filters': ['require_debug_false'],
83 | 'class': 'django.utils.log.AdminEmailHandler'
84 | },
85 | 'console': {
86 | 'level': 'DEBUG',
87 | 'class': 'logging.StreamHandler',
88 | 'formatter': 'verbose',
89 | },
90 | },
91 | 'loggers': {
92 | 'django.request': {
93 | 'handlers': ['mail_admins'],
94 | 'level': 'ERROR',
95 | 'propagate': True
96 | },
97 | 'django.security.DisallowedHost': {
98 | 'level': 'ERROR',
99 | 'handlers': ['console', 'mail_admins'],
100 | 'propagate': True
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/django_config/urls.py:
--------------------------------------------------------------------------------
1 | """django_config URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 | from django.conf import settings
17 | from django.conf.urls import include, url
18 | from django.contrib import admin
19 | from django.views.generic import TemplateView
20 |
21 | urlpatterns = [
22 | url(r'^$', TemplateView.as_view(template_name="index.html")),
23 | url(r'^rest-auth/', include('rest_auth.urls')),
24 | url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
25 | url(r'^admin/', admin.site.urls),
26 | ]
27 |
28 | if settings.DEBUG:
29 | import debug_toolbar
30 | urlpatterns += [
31 | url(r'^__debug__/', include(debug_toolbar.urls)),
32 | ]
33 |
--------------------------------------------------------------------------------
/django_config/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for django_config project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_config.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/docker-compose-development.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | postgres_data_dev: {}
5 | postgres_backup_dev: {}
6 |
7 | services:
8 | postgres:
9 | container_name: postgres_server
10 | build: ./docker_compose/postgres
11 | volumes:
12 | - postgres_data_dev:/var/lib/postgresql/data
13 | - postgres_backup_dev:/backups
14 | env_file: .env
15 |
16 | node:
17 | container_name: node_server
18 | build:
19 | context: .
20 | dockerfile: ./docker_compose/node/development/Dockerfile
21 | command: /start.sh
22 | volumes:
23 | - .:/app
24 | ports:
25 | - "3000:3000"
26 |
27 | django:
28 | container_name: django_server
29 | build:
30 | context: .
31 | dockerfile: ./docker_compose/django/development/Dockerfile
32 | command: /start.sh
33 | depends_on:
34 | - postgres
35 | - node
36 | env_file: .env
37 | volumes:
38 | - .:/app
39 | ports:
40 | - "8000:8000"
41 | links:
42 | - postgres
43 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | postgres_data: {}
5 | postgres_backup: {}
6 |
7 | services:
8 | postgres:
9 | container_name: postgres_server
10 | build: ./docker_compose/postgres
11 | volumes:
12 | - postgres_data:/var/lib/postgresql/data
13 | - postgres_backup:/backups
14 | env_file: .env
15 |
16 | django:
17 | container_name: django_server
18 | build:
19 | context: .
20 | dockerfile: ./docker_compose/django/production/Dockerfile
21 | user: django
22 | depends_on:
23 | - postgres
24 | command: /start.sh
25 | env_file: .env
26 |
27 | nginx:
28 | container_name: nginx_server
29 | build: ./docker_compose/nginx
30 | depends_on:
31 | - django
32 | ports:
33 | - "0.0.0.0:80:80"
34 |
--------------------------------------------------------------------------------
/docker_compose/django/development/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.5
2 |
3 | ENV PYTHONUNBUFFERED 1
4 |
5 | # Setup Debian linux
6 | RUN export DEBIAN_FRONTEND=noninteractive
7 | RUN apt-get update && apt-get -y install build-essential curl
8 | RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
9 | RUN apt-get install -y nodejs
10 | RUN nodejs -v && npm -v
11 |
12 | # Requirements have to be pulled and installed here, otherwise caching won't work
13 | COPY ./requirements /requirements
14 | COPY package.json /app/
15 |
16 | WORKDIR /app
17 | COPY . /app
18 |
19 | RUN pip install -r /requirements/local.txt && npm install && npm run build-development
20 |
21 | COPY ./docker_compose/django/development/start.sh /start.sh
22 | COPY ./docker_compose/django/entrypoint.sh /entrypoint.sh
23 | RUN sed -i 's/\r//' /entrypoint.sh \
24 | && sed -i 's/\r//' /start.sh \
25 | && chmod +x /entrypoint.sh \
26 | && chmod +x /start.sh
27 |
28 | ENTRYPOINT ["/entrypoint.sh"]
29 |
--------------------------------------------------------------------------------
/docker_compose/django/development/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | python manage.py migrate
3 | python manage.py runserver 0.0.0.0:8000
4 |
--------------------------------------------------------------------------------
/docker_compose/django/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | cmd="$@"
4 |
5 | # This entrypoint is used to play nicely with the current cookiecutter configuration.
6 | # Since docker-compose relies heavily on environment variables itself for configuration, we'd have to define multiple
7 | # environment variables just to support cookiecutter out of the box. That makes no sense, so this little entrypoint
8 | # does all this for us.
9 |
10 | # the official postgres image uses 'postgres' as default user if not set explictly.
11 | if [ -z "$POSTGRES_USER" ]; then
12 | export POSTGRES_USER=postgres
13 | fi
14 |
15 | # If not DB is set, then use USER by default
16 | if [ -z "$POSTGRES_DB" ]; then
17 | export POSTGRES_DB=$POSTGRES_USER
18 | fi
19 |
20 | # Need to update the DATABASE_URL if using DOCKER
21 | export DATABASE_URL=postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_DB
22 |
23 |
24 | function postgres_ready(){
25 | python << END
26 | import sys
27 | import psycopg2
28 | try:
29 | conn = psycopg2.connect(dbname="$POSTGRES_DB", user="$POSTGRES_USER", password="$POSTGRES_PASSWORD", host="postgres")
30 | except psycopg2.OperationalError:
31 | sys.exit(-1)
32 | sys.exit(0)
33 | END
34 | }
35 |
36 | until postgres_ready; do
37 | >&2 echo "Postgres is unavailable - sleeping"
38 | sleep 1
39 | done
40 |
41 | >&2 echo "Postgres is up - continuing..."
42 | exec $cmd
43 |
--------------------------------------------------------------------------------
/docker_compose/django/production/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.5
2 |
3 | ENV PYTHONUNBUFFERED 1
4 |
5 | # Setup Debian linux
6 | RUN export DEBIAN_FRONTEND=noninteractive
7 | RUN apt-get update && apt-get -y install build-essential curl
8 | RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
9 | RUN apt-get install -y nodejs
10 | RUN nodejs -v && npm -v
11 |
12 | # Requirements have to be pulled and installed here, otherwise caching won't work
13 | COPY ./requirements /requirements
14 | COPY package.json /app/
15 |
16 | WORKDIR /app
17 | COPY . /app
18 |
19 | RUN pip install -r /requirements/production.txt \
20 | && groupadd -r django \
21 | && useradd -r -g django django \
22 | && npm install && npm run build-production
23 |
24 | RUN chown -R django /app
25 |
26 | COPY ./docker_compose/django/production/start.sh /start.sh
27 | COPY ./docker_compose/django/entrypoint.sh /entrypoint.sh
28 | RUN sed -i 's/\r//' /entrypoint.sh \
29 | && sed -i 's/\r//' /start.sh \
30 | && chmod +x /entrypoint.sh \
31 | && chown django /entrypoint.sh \
32 | && chmod +x /start.sh \
33 | && chown django /start.sh
34 |
35 | ENTRYPOINT ["/entrypoint.sh"]
36 |
--------------------------------------------------------------------------------
/docker_compose/django/production/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | python /app/manage.py collectstatic --noinput
3 | /usr/local/bin/gunicorn django_config.wsgi -w 4 -b 0.0.0.0:5000 --chdir=/app
4 |
--------------------------------------------------------------------------------
/docker_compose/nginx/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:latest
2 | ADD nginx.conf /etc/nginx/nginx.conf
3 |
--------------------------------------------------------------------------------
/docker_compose/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | user nginx;
2 | worker_processes 1;
3 |
4 | error_log /var/log/nginx/error.log warn;
5 | pid /var/run/nginx.pid;
6 |
7 | events {
8 | worker_connections 1024;
9 | }
10 |
11 | http {
12 | include /etc/nginx/mime.types;
13 | default_type application/octet-stream;
14 |
15 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
16 | '$status $body_bytes_sent "$http_referer" '
17 | '"$http_user_agent" "$http_x_forwarded_for"';
18 |
19 | access_log /var/log/nginx/access.log main;
20 |
21 | sendfile on;
22 | #tcp_nopush on;
23 |
24 | keepalive_timeout 65;
25 |
26 | #gzip on;
27 |
28 | upstream app {
29 | server django:5000;
30 | }
31 |
32 | server {
33 | listen 80;
34 | charset utf-8;
35 |
36 |
37 |
38 | location / {
39 | # checks for static file, if not found proxy to app
40 | try_files $uri @proxy_to_app;
41 | }
42 |
43 | # django app
44 | location @proxy_to_app {
45 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
46 | proxy_set_header Host $http_host;
47 | proxy_redirect off;
48 | proxy_pass http://app;
49 |
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/docker_compose/node/development/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:latest
2 |
3 | COPY . /app
4 |
5 | WORKDIR /app
6 | RUN npm install
7 |
8 | COPY ./docker_compose/node/development/start.sh /start.sh
9 | RUN sed -i 's/\r//' /start.sh
10 | RUN chmod +x /start.sh
11 |
--------------------------------------------------------------------------------
/docker_compose/node/development/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | npm run watch
3 |
--------------------------------------------------------------------------------
/docker_compose/postgres/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM postgres:latest
2 |
--------------------------------------------------------------------------------
/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", "django_config.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | # The above import may fail for some other reason. Ensure that the
11 | # issue is really that Django is missing to avoid masking other
12 | # exceptions on Python 2.
13 | try:
14 | import django
15 | except ImportError:
16 | raise ImportError(
17 | "Couldn't import Django. Are you sure it's installed and "
18 | "available on your PYTHONPATH environment variable? Did you "
19 | "forget to activate a virtual environment?"
20 | )
21 | raise
22 | execute_from_command_line(sys.argv)
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "django_reactjs_boilerplate",
3 | "version": "0.1.0",
4 | "description": "A boilerplate for a Django REST/ReactJS project using Redux, Webpack, Hot Reloading, React Hot Reloader 3, Karma, and Mocha",
5 | "main": "index.jsx",
6 | "scripts": {
7 | "build-development": "webpack --config webpack/webpack.local.config.js --progress --colors",
8 | "build-production": "webpack --config webpack/webpack.production.config.js --progress --colors",
9 | "watch": "webpack-dev-server --config webpack/webpack.local.config.js",
10 | "test": "./node_modules/karma/bin/karma start webpack/karma.config.js --log-level debug"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/genomics-geek/django_reactjs_boilerplate.git"
15 | },
16 | "keywords": [
17 | "Django",
18 | "ReactJS",
19 | "Redux",
20 | "React-Hot-Reloader-3",
21 | "Webpack",
22 | "Karma",
23 | "Mocha"
24 | ],
25 | "author": "Michael A. Gonzalez",
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/genomics-geek/django_reactjs_boilerplate/issues"
29 | },
30 | "homepage": "https://github.com/genomics-geek/django_reactjs_boilerplate#readme",
31 | "devDependencies": {
32 | "axios": "^0.15.2",
33 | "babel-cli": "^6.18.0",
34 | "babel-core": "^6.18.0",
35 | "babel-eslint": "^7.1.0",
36 | "babel-loader": "^6.2.6",
37 | "babel-preset-es2015": "^6.18.0",
38 | "babel-preset-react": "^6.16.0",
39 | "babel-preset-stage-2": "^6.18.0",
40 | "css-loader": "^0.25.0",
41 | "deepfreeze": "^2.0.0",
42 | "enzyme": "^2.5.1",
43 | "eslint": "^3.8.1",
44 | "eslint-config-airbnb": "^12.0.0",
45 | "eslint-plugin-import": "^1.16.0",
46 | "eslint-plugin-jsx-a11y": "^2.2.3",
47 | "eslint-plugin-react": "^6.4.1",
48 | "expect": "^1.20.2",
49 | "karma": "^1.3.0",
50 | "karma-babel-preprocessor": "^6.0.1",
51 | "karma-chrome-launcher": "^2.0.0",
52 | "karma-mocha": "^1.2.0",
53 | "karma-sourcemap-loader": "^0.3.7",
54 | "karma-webpack": "^1.8.0",
55 | "lodash": "^4.16.4",
56 | "mocha": "^3.1.2",
57 | "react": "^15.3.2",
58 | "react-bootstrap": "^0.30.5",
59 | "react-cookie": "^0.4.8",
60 | "react-dom": "^15.3.2",
61 | "react-fontawesome": "^1.2.0",
62 | "react-hot-loader": "^3.0.0-beta.6",
63 | "react-redux": "^4.4.5",
64 | "react-router": "^3.0.0",
65 | "react-router-redux": "^4.0.6",
66 | "redux": "^3.6.0",
67 | "redux-devtools": "^3.3.1",
68 | "redux-devtools-dock-monitor": "^1.1.1",
69 | "redux-devtools-log-monitor": "^1.1.0",
70 | "redux-thunk": "^2.1.0",
71 | "style-loader": "^0.13.1",
72 | "webpack": "^1.13.3",
73 | "webpack-bundle-tracker": "0.0.93",
74 | "webpack-dev-server": "^1.16.2"
75 | },
76 | "dependencies": {
77 | "react": "^15.3.2",
78 | "react-dom": "^15.3.2"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/requirements/base.txt:
--------------------------------------------------------------------------------
1 | # Django
2 | django==1.10.2
3 |
4 | # Configuration
5 | dj-database-url==0.4.1
6 | python-decouple==3.0
7 | whitenoise==3.2.2
8 |
9 | # Django 3rd party modules
10 | django-allauth==0.28.0
11 | django-model-utils==2.6
12 | django-rest-auth==0.8.1
13 | django-webpack-loader==0.3.3
14 | djangorestframework==3.5.1
15 | djangorestframework-jwt==1.8.0
16 |
17 | # Python-PostgreSQL Database Adapter
18 | psycopg2==2.6.2
19 |
20 | # Time zones support
21 | pytz==2016.7
22 |
--------------------------------------------------------------------------------
/requirements/local.txt:
--------------------------------------------------------------------------------
1 | # Import all base requirements
2 | -r base.txt
3 |
4 | # Django debug toolbar
5 | django-debug-toolbar==1.6
6 |
7 | # Code linter
8 | pep8==1.7.0
9 |
--------------------------------------------------------------------------------
/requirements/production.txt:
--------------------------------------------------------------------------------
1 | # Import all base requirements
2 | # Pro-tip: Try not to put anything here. Avoid dependencies in production that aren't in development.
3 | -r base.txt
4 |
5 | # WSGI Handler
6 | gunicorn==19.6.0
7 |
8 | # Email backend
9 | django-anymail==0.5
10 |
--------------------------------------------------------------------------------
/static/js/src/example.spec.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect';
2 |
3 | describe('Something abstract', () => {
4 | it('works', () => {
5 | expect(1).toEqual(1);
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/static/js/src/main/components/hello-world.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const HelloWorld = () => {
4 | return (
5 | Hello World!
6 | );
7 | };
8 |
9 | export default HelloWorld;
10 |
--------------------------------------------------------------------------------
/static/js/src/main/containers/DevTools/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createDevTools } from 'redux-devtools';
3 | import LogMonitor from 'redux-devtools-log-monitor';
4 | import DockMonitor from 'redux-devtools-dock-monitor';
5 |
6 |
7 | const DevTools = createDevTools(
8 |
9 |
10 |
11 | );
12 |
13 |
14 | export default DevTools;
15 |
--------------------------------------------------------------------------------
/static/js/src/main/containers/Root/Root.dev.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import { Router } from 'react-router';
4 |
5 | import DevTools from '../DevTools';
6 | import routes from '../../routes';
7 |
8 |
9 | const Root = ({ store, history }) => {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | Root.propTypes = {
21 | store: React.PropTypes.object.isRequired,
22 | history: React.PropTypes.object.isRequired
23 | };
24 |
25 |
26 | export default Root;
27 |
--------------------------------------------------------------------------------
/static/js/src/main/containers/Root/Root.prod.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import { Router } from 'react-router';
4 |
5 | import routes from '../../routes';
6 |
7 |
8 | const Root = ({ store, history }) => {
9 | return (
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | Root.propTypes = {
17 | store: React.PropTypes.object.isRequired,
18 | history: React.PropTypes.object.isRequired
19 | };
20 |
21 |
22 | export default Root;
23 |
--------------------------------------------------------------------------------
/static/js/src/main/containers/Root/index.jsx:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV === 'production') {
2 | module.exports = require('./Root.prod');
3 | } else {
4 | module.exports = require('./Root.dev')
5 | }
6 |
--------------------------------------------------------------------------------
/static/js/src/main/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { AppContainer } from 'react-hot-loader';
4 | import { browserHistory } from 'react-router';
5 | import { syncHistoryWithStore } from 'react-router-redux';
6 |
7 | import Root from './containers/Root';
8 | import configureStore from './store';
9 |
10 |
11 | const store = configureStore();
12 | const history = syncHistoryWithStore(browserHistory, store);
13 |
14 |
15 | ReactDOM.render(
16 |
17 |
18 | ,
19 | document.getElementById('react-root')
20 | );
21 |
22 | if (module.hot) {
23 | module.hot.accept('./containers/Root', () => {
24 | const Root = require('./containers/Root').default;
25 | ReactDOM.render(
26 |
27 |
28 | ,
29 | document.getElementById('react-root')
30 | );
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/static/js/src/main/reducers/index.jsx:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 |
5 | // Import reducers here
6 |
7 | const reducers = {};
8 |
9 | const rootReducer = combineReducers({
10 | ...reducers,
11 | routing: routerReducer
12 | });
13 |
14 |
15 | export default rootReducer;
16 |
--------------------------------------------------------------------------------
/static/js/src/main/routes.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route } from 'react-router';
3 |
4 | import HelloWorld from './components/hello-world';
5 |
6 |
7 | const routes = (
8 |
9 | );
10 |
11 |
12 | export default routes;
13 |
--------------------------------------------------------------------------------
/static/js/src/main/store/configureStore.dev.jsx:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import thunk from 'redux-thunk';
3 |
4 | import DevTools from '../containers/DevTools';
5 | import rootReducer from '../reducers';
6 |
7 |
8 | const enhancer = compose(
9 | // Middleware you want to use in development
10 | applyMiddleware(thunk),
11 | // Required! Enable Redux DevTools with the monitors you chose
12 | DevTools.instrument()
13 | );
14 |
15 |
16 | const configureStore = (initialState) => {
17 | // Note: only Redux >= 3.1.0 supports passing enhancer as third argument
18 | // See: https://github.com/rackt/redux/releases/tag/v3.1.0
19 | const store = createStore(rootReducer, initialState, enhancer);
20 |
21 | // Hot Reload reducers
22 | // Note: Requires Webpack or Browserify HMR to be enabled
23 | if (module.hot) {
24 | module.hot.accept('../reducers', () =>
25 | store.replaceReducer(require('../reducers').default)
26 | );
27 | }
28 | return store;
29 | };
30 |
31 |
32 | export default configureStore;
33 |
--------------------------------------------------------------------------------
/static/js/src/main/store/configureStore.prod.jsx:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunk from 'redux-thunk';
3 |
4 | import rootReducer from '../reducers';
5 |
6 |
7 | // Middleware you want to use in production
8 | const enhancer = applyMiddleware(thunk);
9 |
10 | const configureStore = (initialState) => {
11 | // Note: only Redux >= 3.1.0 supports passing enhancer as third argument
12 | // See: https://github.com/rackt/redux/releases/tag/v3.1.0
13 | return createStore(rootReducer, initialState, enhancer);
14 | };
15 |
16 |
17 | export default configureStore;
18 |
--------------------------------------------------------------------------------
/static/js/src/main/store/index.jsx:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV === 'production') {
2 | module.exports = require('./configureStore.prod');
3 | } else {
4 | module.exports = require('./configureStore.dev')
5 | }
6 |
--------------------------------------------------------------------------------
/static/js/src/test_index.js:
--------------------------------------------------------------------------------
1 | var testsContext = require.context('.', true, /.spec$/);
2 | testsContext.keys().forEach(testsContext);
3 |
--------------------------------------------------------------------------------
/static/js/src/utilities/api.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import * as cookie from './cookie';
3 |
4 |
5 | const api = axios.create({
6 | baseURL: process.env.BASE_URL,
7 | timeout: 1000,
8 | });
9 |
10 | // CRSF token is needed in all requests that can make a change server side
11 | api.defaults.headers.post['X-CSRFToken'] = cookie.csrftoken;
12 | api.defaults.headers.put['X-CSRFToken'] = cookie.csrftoken;
13 | api.defaults.headers.patch['X-CSRFToken'] = cookie.csrftoken;
14 | // api.defaults.headers.delete['X-CSRFToken'] = cookie.csrftoken; // Currently axios can't set headers for DELETE
15 |
16 | // Since we will only be using JSON APIs, add Content-Type: application/json to header as default
17 | api.defaults.headers.post['Content-Type'] = 'application/json';
18 | api.defaults.headers.put['Content-Type'] = 'application/json';
19 | api.defaults.headers.patch['Content-Type'] = 'application/json';
20 |
21 | // Since we will only be using JSON APIs, add Accept: application/json to header as default
22 | api.defaults.headers.get.Accept = 'application/json';
23 | api.defaults.headers.post.Accept = 'application/json';
24 | api.defaults.headers.put.Accept = 'application/json';
25 | api.defaults.headers.patch.Accept = 'application/json';
26 |
27 | // JWT is going to be saved into cookie
28 | // cookies.save('jwt', response.data.token, { secure: true, httpOnly: true });
29 | // Therefore it will automatically be sent in the header of all API requests
30 | // JWT will not be accessible to JavaScript because it is httpOnly :)
31 |
32 | export default api;
33 |
--------------------------------------------------------------------------------
/static/js/src/utilities/cookie.js:
--------------------------------------------------------------------------------
1 | import cookies from 'react-cookie';
2 |
3 |
4 | // This is the place where we can load elements from a cookie to be used in our app
5 |
6 | // Django CRSF Token is stored in a cookie
7 | const csrftoken = cookies.load('csrftoken');
8 |
9 | // JWT is going to be saved into cookie
10 | // cookies.save('jwt', response.data.token, { secure: true, httpOnly: true });
11 | // Therefore it will automatically be sent in the header of all API requests
12 | // JWT will NOT be accessible to JavaScript because it is httpOnly :)
13 |
14 | export { csrftoken };
15 |
--------------------------------------------------------------------------------
/webpack/karma.config.js:
--------------------------------------------------------------------------------
1 | var webpackConfig = require('./webpack.local.config.js');
2 |
3 | webpackConfig.entry = {};
4 |
5 | module.exports = function (config) {
6 | config.set({
7 |
8 | // base path that will be used to resolve all patterns (eg. files, exclude)
9 | basePath: '',
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['mocha'],
14 |
15 | // list of files / patterns to load in the browser
16 | files: [
17 | '../static/js/src/test_index.js'
18 | ],
19 |
20 | // list of files to exclude
21 | exclude: [],
22 |
23 | // preprocess matching files before serving them to the browser
24 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
25 | preprocessors: {
26 | '../static/js/src/test_index.js': ['webpack', 'sourcemap'],
27 | },
28 |
29 | // test results reporter to use
30 | // possible values: 'dots', 'progress'
31 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
32 | reporters: ['progress'],
33 |
34 | // web server port
35 | port: 9876,
36 |
37 | // enable / disable colors in the output (reporters and logs)
38 | colors: true,
39 |
40 | // level of logging
41 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
42 | logLevel: config.LOG_INFO,
43 |
44 | // enable / disable watching file and executing tests whenever any file changes
45 | autoWatch: true,
46 | autoWatchBatchDelay: 300,
47 |
48 | // start these browsers
49 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
50 | browsers: ['Chrome'],
51 |
52 | // Continuous Integration mode
53 | // if true, Karma captures browsers, runs the tests and exits
54 | singleRun: false,
55 |
56 | // Concurrency level
57 | // how many browser should be started simultaneous
58 | concurrency: Infinity,
59 |
60 | // Webpack
61 | webpack: webpackConfig,
62 | webpackServer: {
63 | noInfo: true
64 | }
65 | });
66 | };
67 |
--------------------------------------------------------------------------------
/webpack/webpack.base.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | module: {
4 | loaders: [{
5 | test: /\.(js|jsx)$/,
6 | exclude: /node_modules/,
7 | loader: 'babel-loader',
8 | query: {
9 | presets: ['es2015', 'stage-2', 'react']
10 | }
11 | }, {
12 | test: /\.css$/,
13 | loader: 'style-loader!css-loader'
14 | }]
15 | },
16 |
17 | resolve: {
18 | modulesDirectories: ['node_modules'],
19 | extensions: ['', '.js', '.jsx']
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/webpack/webpack.local.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var BundleTracker = require('webpack-bundle-tracker');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.base.config.js');
5 |
6 | config.entry = {
7 | main: [
8 | 'webpack-dev-server/client?http://0.0.0.0:3000',
9 | 'webpack/hot/only-dev-server',
10 | 'react-hot-loader/patch',
11 | path.join(__dirname, '../static/js/src/main/index')
12 | ]
13 | };
14 |
15 | config.devtool = 'inline-sourcemap';
16 | config.output = {
17 | path: path.join(__dirname, '../static/js/builds-development/'),
18 | filename: '[name]-[hash].js',
19 | publicPath: 'http://0.0.0.0:3000/static/builds/',
20 | };
21 |
22 | config.plugins = [
23 | new webpack.HotModuleReplacementPlugin(),
24 | new BundleTracker({ filename: './webpack/webpack-stats.dev.json' }),
25 | new webpack.DefinePlugin({
26 | 'process.env': {
27 | NODE_ENV: JSON.stringify('development'),
28 | BASE_URL: JSON.stringify('http://0.0.0.0:8000/'),
29 | }
30 | })
31 | ];
32 |
33 | config.module.loaders[0].query.plugins = ['react-hot-loader/babel'];
34 |
35 | config.devServer = {
36 | inline: true,
37 | progress: true,
38 | hot: true,
39 | historyApiFallback: true,
40 | host: '0.0.0.0',
41 | port: 3000
42 | };
43 |
44 | module.exports = config;
45 |
--------------------------------------------------------------------------------
/webpack/webpack.production.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var BundleTracker = require('webpack-bundle-tracker');
4 | var config = require('./webpack.base.config.js');
5 |
6 | config.entry = {
7 | main: [
8 | path.join(__dirname, '../static/js/src/main/index')
9 | ]
10 | };
11 |
12 | config.output = {
13 | path: path.join(__dirname, '../static/js/builds/'),
14 | filename: '[name]-[hash].min.js',
15 | publicPath: '/static/js/builds/'
16 | };
17 |
18 | config.plugins = [
19 | new BundleTracker({ filename: './webpack/webpack-stats.production.json' }),
20 | new webpack.optimize.DedupePlugin(),
21 | new webpack.optimize.OccurenceOrderPlugin(),
22 | new webpack.DefinePlugin({
23 | 'process.env': {
24 | NODE_ENV: JSON.stringify('production'),
25 | BASE_URL: JSON.stringify('http://0.0.0.0/'),
26 | }
27 | }),
28 | new webpack.optimize.UglifyJsPlugin({
29 | mangle: false,
30 | sourcemap: false
31 | })
32 | ];
33 |
34 | module.exports = config;
35 |
--------------------------------------------------------------------------------