/ uberspace

Setting up a self hosted recipe management site - OpenEats

How do you save your favorite recipes? Do a quick Google search? Bookmark hundreds of links to different recipe websites? Or even write them down in a book, the old-fashioned analogue way? Why not storing them all in one place?

OpenEats project

OpenEats is a recipe management site that allows users to create, share, and store recipes. OpenEats was created using django, a python web framework and several django plugins.

Features:

  • Create, share and edit your own recipes
  • Mark your own recipes as private so only you can see them
  • Follow other chefs on OpenEats and see what they are rating, and adding
  • Create custom grocery list
  • Add recipes to your grocery lists
  • Share grocery list with other people. This is handy for a family to have a list everyone can update
  • Rate recipes
  • Comment on recipes
  • 100% OpenSource and free
  • A complete administration backend to maintain your site
  • Export recipes to PDF or MealMaster format
  • Internationalization Support

This all sounds really nice. Sadly, it seems like the project is discontinued, as the latest commit to its source on GitHub was made about two years ago. Many of the python dependencies are outdated, in particular the web framework Django. Another user however has ported OpenEats to a newer version of Django and made it ready for deploying via docker.

Host OpenEats on uberspace.de

Docker is not available on uberspace, but everything else (python, pip, ...) is, so let's get started.

The first thing we want to set up is virtualenv and virtualenvwrapper. These two packages make handling multiple python development environments and especially different dependencies really easy.

pip2.7 install virtualenv --user
pip2.7 install virtualenvwrapper --user

Next we will set up some variables that virtualenv and virtualenvwrapper depend upon and store them for later use. Open up the file .bashrc and add the following lines

export WORKON_HOME=~/Envs
export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python2.7
source bin/virtualenvwrapper.sh

The first variable WORKON_HOME can be set to anything you like. This will be the folder where the libraries and packages of the virtual environment will be stored. Save and close your file. Then, type in the following two commands:

source ~/.bashrc
mkdir -p $WORKON_HOME

This will update our variables und create our virtual environment folder. The next two lines will create our virtual environment and start the working process on it. You'll notice this, because the name you choose (here it's openeats) will be put in front of your input prompt in brackets.

mkvirtualenv openeats --no-site-packages
workon openeats
# before
[user@host ~]$
# after
(openeats) [user@host ~]$

The next few steps will download the source code for OpenEats. As I've mentioned before, the user pando85 did a great job of updating OpenEats to a more recent version of Django, but also adapted the projects structure for docker usage. We need to fix this I fixed this.
In my repository I removed some docker specific files and moved the application folder, to represent a more common Django project structure.

cd
git clone https://github.com/DeEgge/openeats.git

Let's install any additional packages need by OpenEats. This may take a while, so stretch your back!

pip2.7 install -r openeats/requirements.txt

Strech your back

The next step on our list is the database. OpenEats supports a variety of DBs, and depending on how big your recipe collection is going to be, you're going to want to use PostgreSQL or MySQL. But SQLite will do, too.
Create an empty SQLite file with the following command

sqlite3 ~/openeats/openeats/openeats.db ".databases"

Next, we are going to configure our project regarding database and URL. Open up the file settings.py (nano ~/openeats/openeats/settings.py) and make some changes.

...
	DATABASES = {
	    'default': {
	        'ENGINE': 'django.db.backends.sqlite3',  # Add sqlite3 after .
	        'NAME': '/home/$USER/openeats/openeats/openeats.db',  # don't use relative path $USER -> username
	        'USER': '',  # Not used with sqlite3.
	        'PASSWORD': '',  # Not used with sqlite3.
	        'HOST': '',  # Set to empty string for localhost. Not used with sqlite3.
	        'PORT': '',  # Set to empty string for default. Not used with sqlite3.
	    }
	}
...
	TIME_ZONE = 'Europe/Berlin'
	USE_TZ = True. # Add this line
...
	LANGUAGE_CODE = 'de-de'
...
	USE_X_FORWARDED_HOST = True  # Add this line, too
...
	ALLOWED_HOSTS = ['openeats.username.hostname.uberspace.de']  # Change this to your desired domain name. Any domain that is activated on your uberspace will do. Remember this!
...

Now that we configured the database, let's fill it up with some rudimentary data.

cd ~/openeats
./manage.py makemigrations
./manage.py migrate
./manage.py collectstatic --noinput --clear
./manage.py createsuperuser

After the last line, you will be prompted to create a superuser for your recipe management site. Later you will be able to access the admin backend with your username and password, so keep that in mind.

It is advised to work on a Django project within your $HOME directory, which is what we were doing the last couple of minutes. But since we are building a web application, some files need to be served from through our DOCUMENT_ROOT. These static files were gathered through the collectstatic command that we just typed in. We are going to copy them to our DOCUMENT_ROOT.
Remember that domain we specified in the settings.py file? We'll create a directory for that and move the static files inside that directory, so that the web server can serve them.

OPENEATS_URL=openeats.$USER.$(hostname)
mkdir /var/www/virtual/$USER/$OPENEATS_URL
cp -a ~/openeats/openeats/static /var/www/virtual/$USER/$OPENEATS_URL/

We're nearly there. Apply some minor fixtures provided by pando85.

cd ~/openeats
./manage.py loaddata openeats/accounts/fixtures/test_user_data.json
./manage.py loaddata openeats/list/fixtures/list_test_data.json
./manage.py loaddata openeats/list/fixtures/aisle_data.json  
./manage.py loaddata openeats/accounts/fixtures/test_friend_data.json
./manage.py loaddata openeats/recipe_groups/fixtures/course_data.json
./manage.py loaddata openeats/recipe_groups/fixtures/cuisine_data.json
./manage.py loaddata openeats/recipe/fixtures/recipe_data.json
./manage.py loaddata openeats/ingredient/fixtures/ing_data.json

All the necessary configuration is done, let's find an open port and fire up a server. Type in the following command to find a open port and set that to the variable $OPENEATSPORT. If the output is empty, you're set. If not, just try again.

OPENEATSPORT=$(( $RANDOM % 4535 + 61000)); netstat -tulpen | grep $OPENEATSPORT && echo "try again"

Let's test our server.

Gunicorn is a python HTTP server used for serving WSGI applications. Since we've installed it as one of the required packages, we can use it directly from within our virtual environment.

/home/$USER/Envs/openeats/bin/gunicorn --error-logfile - --reload --chdir /home/$USER/openeats --bind 127.0.0.1:$OPENEATSPORT openeats.wsgi:application

If everything went right, we should get something like this:

2016-07-24 14:26:56.372297500 2016-07-24 14:26:56 [18606] [INFO] Starting gunicorn 19.0.0
2016-07-24 14:26:56.372824500 2016-07-24 14:26:56 [18606] [INFO] Listening at: http://127.0.0.1:<yourPort> (18606)
2016-07-24 14:26:56.372956500 2016-07-24 14:26:56 [18606] [INFO] Using worker: sync
2016-07-24 14:26:56.374943500 2016-07-24 14:26:56 [18629] [INFO] Booting worker with pid: 18629

End that process hitting Ctrl+C. We can't access our domain yet, because the port is still closed to the outside. We need to use proxy rewrite rules to open our application to the outside world.

cat <<__EOF__> /var/www/virtual/$USER/$OPENEATS_URL/.htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteBase /
RewriteRule ^(.*)$ http://127.0.0.1:$OPENEATSPORT/\$1 [P]
 
RequestHeader set X-Forwarded-Proto https env=HTTPS
__EOF__

Now, if we run the Gunicorn command again, we should be able to access OpenEats at http://openeats.username.hostname.uberspace.de. We can finally set up a daemon for our application, that keeps the Gunicorn process running.

uberspace-setup-service openeats /home/$USER/Envs/openeats/bin/gunicorn --error-logfile - --reload --chdir /home/$USER/openeats --bind 127.0.0.1:$OPENEATSPORT openeats.wsgi:application

That's it. Now we have ourselves a nice recipe management site. Go ahead, access the http://openeats.username.hostname.uberspace.de/admin area and create some user profiles. And then some recipes.

Happy coding... I mean, cooking!

Happy cooking


Sources