Skip to content

Commit

Permalink
Merge pull request RyanNoelk#57 from RyanNoelk/dev
Browse files Browse the repository at this point in the history
Production Ready Docker
  • Loading branch information
RyanNoelk authored Jan 8, 2017
2 parents 3162854 + b518a1c commit e14d758
Show file tree
Hide file tree
Showing 48 changed files with 346 additions and 179 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ site-media/
openeats.db
*.css.map
*.js.map
templates/angular/
api/templates/angular/
frontend/node_modules/
frontend/.DS_Store
*.DS_Store
static-files/
database/
frontend/build/

servies/static-files/
servies/side-media/
env_prod.list
101 changes: 35 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,96 +2,65 @@

OpenEats is a recipe management site that allows users to create, share, and store recipes. This fork uses Django Rest Framework as a backend and React (with flux) as a front end.

The main goals of this project are two fold. One, I wanted a place to store my personal collection of recipes and share them with close friends and family. Two, I wanted to learn react :). I went digging around for a starting point and gathering ideas when i came across open eats. It had some cool ideas and was well documented for the most part.
The main goals of this project are twofold. One, I wanted a place to store my personal collection of recipes and share them with close friends and family. Two, I wanted to learn React :). I went digging around for a starting point and gathering ideas when I came across open eats. It had some cool ideas and was well documented for the most part.

I have a lot of ideas as far as features go. But since I moved the whole UI to react and the backend to a pure API, I'm currently working on getting the core of the project stable. The Core, in my mind, consists of a few things.
I have a lot of ideas as far as features go. But since I moved the whole UI to React and the backend to a pure API, I'm currently working on getting the core of the project stable. The Core, in my mind, consists of a few things.

- Creating, viewing, and editing recipes.
- Browsing and searching for recipes.
- A simple homepage and about page.

# Running the App

## Docker
The recommended way to run this app is with docker. you can install docker [here](https://www.docker.com/products/overview). If you are not familiar with docker you can read more about it on there [website](https://www.docker.com/what-docker).

```bash
docker-compose build
docker-compose up -d
```
If you are looking to run the app without docker, see the instructions [here](docs/Running_without_Docker.md)

You can then use `docker-compose run --rm api bash` to run the DB migrations shown below (eg: `python ./manage.py migrate`).
### Running the app with docker for production

If you are looking to run this in production, there is no need to clone the repo.

## Manually
First, create two files:
- docker-prod.yml - This file can be found in the in the repo.
- env_prod.list - The settings file `env_stg.list` can be used as an example.

* `git clone https://github.com/RyanNoelk/OpenEats.git`
* `cd openeats`
* `git checkout dev`
* Create a python virtual environment (I use [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)).
* `pip install -r base/requirements.txt`
The `docker-prod.yml` contains the list of images and commands to run the app. It come with an nginx reverse proxy that by default will run on port 80. You will most likely want to change the port that nginx runs on as well as use a fix tag for the image. By default all are set to latest.

Next we need to create a `base/local_settings.py` file for configuring your particulate instance. Things like your secret key and database settings should go here.
Most of the settings in your `env_prod.list` can stay the same as `env_stg.list` that is in this repo. It is highly recommended to change the django secret key when running this app. Once the files have been created run:

```bash
docker-compose -f docker-prod.yml up -d
```

Now that we have the dependencies. We need to setup the DB. The default is sqlite, which is fine for dev purposes. But if you want something a bit more robust or if you want to run this in a production environment you should use something like postgres or mysql. The current configuration should work with any database you choose to use. See the [django database docs](https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-DATABASES) for setting up your database. As a side note, please make sure that you install any additional drivers that python needs to work with the database that you choose.
### Running the app with docker for development
```bash
git clone https://github.com/RyanNoelk/OpenEats.git
cd openeats
git checkout dev
docker-compose build
docker-compose up -d
```

The migrations should all be present in the repo. Once the database is installed, migrate all the apps.
* `./manage.py migrate`
### First Time Setup

Now lets create a super user as an admin for the site. Run `./manage.py createsuperuser` and follow the prompts.
Regardless of if your running the app in production or development, you need to seed the database.

Once you have created a user, we can add some base data to the project. This will load some basic course and cuisine data into the project.
* `./manage.py loaddata course_data.json`
* `./manage.py loaddata cuisine_data.json`
Run `docker-compose run --rm api bash` to open a bash shell to the API.
```bash
./manage.py migrate
./manage.py collectstatic
./manage.py createsuperuser
./manage.py loaddata course_data.json
./manage.py loaddata cuisine_data.json
```

If you want to add some test data we can load a few recipes and some news data. This data isn't really needed unless you just wanna see how the app looks and if its working.
* `./manage.py loaddata news_data.json`
* `./manage.py loaddata recipe_data.json`
* `./manage.py loaddata ing_data.json`
* `./manage.py loaddata direction_data.json`

Now we need to gather all the static fields for the django rest framework and django admin panel. Also, we need to build the default image for when recipes don't have an image.

* `./manage collectstatic`
* `./manage imgur`

That should be it! you can run the dev server now to load up the api `./manage runserver`.
# Contributing

Load up `http://localhost:8000/admin/` in a browser and see the results.

The next thing we need to do is setup react. The bundle and index are located in `frontend/public/` The bundle there is already a production copy. That should be used if your looking to just serve the project with, for example, nginx. However if you want node to serve the code. See below

From your base dir:
* `cd frontend`
* `npm install` (this will take a few minutes as its loading a ton of small static files)
* `NODE_ENV=production npm start`
* Load `http://localhost:8080` in a browse and see the results.

If you just want to test or play around with the code, you can run the dev server. First, make sure the django server is running. Then:
* `cd frontend`
* `npm install` (this will take a few minutes as its loading a ton of small static files)
* `npm start`
* Load `http://localhost:8080` in a browse and see the results.

# Dev Tips

All of the data (excluding recipes) can be added to the DB using the Django REST GUI. The following is the CURL post I use to add recipes to the DB quickly for testing. You'll will need to either add your auth token as a header or disable the auth check in `api/v1/recipe/seralizers.py`.

```bash
curl -X POST -H "Content-Type: application/json" http://localhost:8000/api/v1/recipe/recipes/ --data '{"info":"hi", "cook_time":"12", "title":"hi", "directions":[{"title":"do this first", "step":"1"}], "servings":"12", "cuisine":"1", "ingredients":[{"title":"first", "quantity":"2", "measurement":"tsp"}], "prep_time":"123", "course":"1", "tags":[{"author":"1", "title":"chicken"}]}'
```

Apps can access there API roots via there app names:
* Recipes - http://localhost:8000/api/v1/recipe
* Ingredients - http://localhost:8000/api/v1/ingredient/
* Recipe groups - http://localhost:8000/api/v1/recipe_groups/
* News - http://localhost:8000/api/v1/news/
* Lists - http://localhost:8000/api/v1/list/

### Generating updated locale files

After adding new `defineMessages` you'll need to update the locale files. Instead of doing it manually you can run this script to do it for you.

```bash
docker-compose run --rm node bash
./node_modules/.bin/babel-node scripts/merge-locale.js
```
All contributions are welcome! If you are are having an problem or find a bug please create an issue in github. If you would like to contribute, feel free to grab any unassigned issue with github issues.
6 changes: 0 additions & 6 deletions __init__.py

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes
9 changes: 4 additions & 5 deletions services/gunicorn_start.sh → api/base/gunicorn_start.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash

NAME="OpenEats" # Name of the application
DJANGODIR=/home/ryan/www/openeats # Django project directory
USER=www-data # the user to run as
GROUP=www-data # the group to run as
DJANGODIR=/code # Django project directory
USER=root # the user to run as
GROUP=root # the group to run as
NUM_WORKERS=5 # how many worker processes should Gunicorn spawn
DJANGO_SETTINGS_MODULE=base.settings # which settings file should Django use
DJANGO_WSGI_MODULE=base.wsgi # WSGI module name
Expand All @@ -12,7 +12,6 @@ echo "Starting $NAME as `whoami`"

# Activate the virtual environment
cd $DJANGODIR
source /home/ryan/.virtualenvs/openeats/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH

Expand All @@ -22,4 +21,4 @@ exec gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $NUM_WORKERS \
--user=$USER --group=$GROUP \
--bind=localhost:8001
--bind=$API_URL
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
45 changes: 17 additions & 28 deletions base/settings.py → api/base/settings.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# Django settings for openeats project.
import os

DEBUG = True
DEBUG = os.environ.get('DJANGO_DEBUG', True)
SERVE_MEDIA = True

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
PROJECT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Make this unique, and don't share it with anybody.
SECRET_KEY = 'tk1ig_pa_p9^muz4vw4%#q@0no$=ce1*b$#s343jouyq9lj)k33j('
SECRET_KEY = os.environ.get('DJANGO_SECERT_KEY', 'ChangeMe!')

SITE_ID = 1

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'openeats', # Or path to database file if using sqlite3.
'USER': 'root', # Not used with sqlite3.
'PASSWORD': 'root', # Not used with sqlite3.
'HOST': 'db', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '3306', # Set to empty string for default. Not used with sqlite3.
'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ.get('MYSQL_DATABASE', 'openeats'),
'USER': 'root',
'PASSWORD': os.environ.get('MYSQL_ROOT_PASSWORD', ''),
'HOST': 'db',
'PORT': '3306',
}
}
# Hosts/domain names that are valid for this site; required if DEBUG is False
Expand Down Expand Up @@ -80,11 +80,11 @@
'coreapi',

'base',
'api.v1.recipe',
'api.v1.recipe_groups',
'api.v1.ingredient',
'api.v1.news',
'api.v1.list',
'v1.recipe',
'v1.recipe_groups',
'v1.ingredient',
'v1.news',
'v1.list',

'imagekit',
'django_extensions',
Expand Down Expand Up @@ -114,7 +114,7 @@

# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
TIME_ZONE = 'America/Chicago'
TIME_ZONE = os.environ.get('DJANGO_TIME_ZONE', 'America/Chicago')

# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
Expand All @@ -132,7 +132,7 @@
}

CORS_ORIGIN_WHITELIST = (
'localhost:8080'
os.environ.get('NODE_URL', 'localhost:8080')
)

# Static and i18n settings
Expand All @@ -142,11 +142,11 @@
)

LOCALE_PATHS = (
os.path.join(PROJECT_PATH, '../locale', ),
os.path.join(PROJECT_PATH, 'locale', ),
)

FIXTURE_DIRS = [
os.path.join(PROJECT_PATH, 'api', 'v1', 'fixtures'),
os.path.join(PROJECT_PATH, 'v1', 'fixtures'),
]

# If you set this to False, Django will make some optimizations so as not
Expand All @@ -161,9 +161,6 @@
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = os.path.join(PROJECT_PATH, 'site-media')
STATIC_ROOT = os.path.join(PROJECT_PATH, 'static-files')
STATICFILES_DIRS = [
os.path.join(PROJECT_PATH, "static"),
]

# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
Expand All @@ -177,14 +174,6 @@
('de', ugettext('German')),
)

#Email Server Settings
DEFAULT_FROM_EMAIL = ''
EMAIL_HOST = ''
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_PORT =''
#EMAIL_USE_TLS = True

try:
from local_settings import *
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion base/urls.py → api/base/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

urlpatterns = [
# Backend
url(r'^api/', include('api.urls')),
url(r'^api/v1/', include('v1.urls', namespace='v1')),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),

# Generic Static Home
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 0 additions & 9 deletions api/urls.py

This file was deleted.

2 changes: 1 addition & 1 deletion api/v1/accounts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python
# encoding: utf-8

default_app_config = 'api.v1.accounts.apps.ApiConfig'
default_app_config = 'v1.accounts.apps.ApiConfig'
2 changes: 1 addition & 1 deletion api/v1/ingredient/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import unicode_literals

from django.contrib import admin
from api.v1.ingredient.models import Ingredient
from v1.ingredient.models import Ingredient


class IngredientAdmin(admin.ModelAdmin):
Expand Down
2 changes: 1 addition & 1 deletion api/v1/ingredient/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.utils.translation import ugettext_lazy as _
from measurement.measures import Volume

from api.v1.recipe.models import Recipe
from v1.recipe.models import Recipe
from .utils import mass_to_volume_lookup


Expand Down
2 changes: 1 addition & 1 deletion api/v1/ingredient/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from .models import Ingredient
from .serializers import IngredientSerializer
from api.v1.common.permissions import IsOwnerOrReadOnly
from v1.common.permissions import IsOwnerOrReadOnly


class IngredientViewSet(viewsets.ModelViewSet):
Expand Down
2 changes: 1 addition & 1 deletion api/v1/list/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django_extensions.db.fields import AutoSlugField
from api.v1.recipe.models import Recipe
from v1.recipe.models import Recipe


class GroceryList(models.Model):
Expand Down
2 changes: 1 addition & 1 deletion api/v1/list/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from django.core.urlresolvers import reverse
from django.forms.models import inlineformset_factory
from django.utils.translation import ugettext as _
from api.v1.recipe.models import Recipe
from v1.recipe.models import Recipe
from datetime import date

from django.http import HttpResponse
Expand Down
2 changes: 1 addition & 1 deletion api/v1/news/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from .models import News
from .serializers import NewsSerializer
from api.v1.common.permissions import IsAdminOrReadOnly
from v1.common.permissions import IsAdminOrReadOnly


class NewsViewSet(viewsets.ModelViewSet):
Expand Down
2 changes: 1 addition & 1 deletion api/v1/recipe/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.contrib import admin
from .models import Recipe
from imagekit.admin import AdminThumbnail
from api.v1.ingredient.models import Ingredient
from v1.ingredient.models import Ingredient
from django.shortcuts import render_to_response
from django.conf import settings

Expand Down
2 changes: 1 addition & 1 deletion api/v1/recipe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill

from api.v1.recipe_groups.models import Cuisine, Course, Tag
from v1.recipe_groups.models import Cuisine, Course, Tag


class Recipe(models.Model):
Expand Down
Loading

0 comments on commit e14d758

Please sign in to comment.