From 3d8c6a24a2582a2b7ec558c6e7a1603211f4e7c8 Mon Sep 17 00:00:00 2001 From: Oliver Marks Date: Sun, 5 Feb 2017 12:59:20 +0000 Subject: [PATCH] stage config, and contact form setup to send email --- compose/django/gunicorn.sh | 4 +- compose/nginx/Dockerfile | 2 +- compose/nginx/nginx-secure.conf | 58 +++---- compose/nginx/start.sh | 32 ++-- config/settings/production.py | 6 +- config/settings/stage.py | 217 ++++++++++++++++++++++++ docker-compose.yml | 2 +- mhackspace/contact/forms.py | 2 +- mhackspace/contact/views.py | 19 ++- mhackspace/templates/base.html | 7 +- mhackspace/templates/pages/contact.html | 8 +- mhackspace/templates/pages/home.html | 1 - stage.yml | 77 +++++++++ staticfiles/staticfiles | 0 14 files changed, 365 insertions(+), 70 deletions(-) create mode 100644 config/settings/stage.py create mode 100644 stage.yml mode change 100644 => 100755 staticfiles/staticfiles diff --git a/compose/django/gunicorn.sh b/compose/django/gunicorn.sh index 52c50ca..62f6668 100644 --- a/compose/django/gunicorn.sh +++ b/compose/django/gunicorn.sh @@ -1,6 +1,4 @@ #!/bin/sh python /app/manage.py collectstatic --noinput -chmod 777 -R /data/sockets/ -touch /data/sockets/gunicron.sock -ls -la /data/sockets/ +python /app/manage.py compilescss /usr/local/bin/gunicorn config.wsgi -w 4 -b unix:/data/sockets/gunicorn.sock --chdir=/app diff --git a/compose/nginx/Dockerfile b/compose/nginx/Dockerfile index 136fd5d..dd4d6bf 100644 --- a/compose/nginx/Dockerfile +++ b/compose/nginx/Dockerfile @@ -3,7 +3,7 @@ ADD nginx.conf /etc/nginx/nginx.conf ADD start.sh /start.sh -ADD nginx-secure.conf /etc/nginx/nginx-secure.conf +#ADD nginx-secure.conf /etc/nginx/nginx-secure.conf #ADD dhparams.pem /etc/ssl/private/dhparams.pem CMD /start.sh diff --git a/compose/nginx/nginx-secure.conf b/compose/nginx/nginx-secure.conf index 10a53ee..43f8963 100755 --- a/compose/nginx/nginx-secure.conf +++ b/compose/nginx/nginx-secure.conf @@ -30,51 +30,33 @@ http { gzip on; upstream app { - server django:5000; + server unix:/data/sockets/gunicorn.sock; + # server django:5000; } + server { listen 80; server_name ___my.example.com___ www.___my.example.com___; - location /.well-known/acme-challenge { - # Since the certbot container isn't up constantly, need to resolve ip dynamically using docker's dns - resolver ___NAMESERVER___; - set $certbot_addr_port certbot:80; - proxy_pass http://$certbot_addr_port; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - } +# ssl on; +# ssl_certificate /etc/letsencrypt/live/___my.example.com___/fullchain.pem; +# ssl_certificate_key /etc/letsencrypt/live/___my.example.com___/privkey.pem; +# ssl_session_timeout 5m; +# ssl_protocols TLSv1 TLSv1.1 TLSv1.2; +# ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; +# ssl_prefer_server_ciphers on; - location / { - return 301 https://$server_name$request_uri; - } +# ssl_session_cache shared:SSL:10m; +# ssl_dhparam /etc/ssl/private/dhparams.pem; - } - - server { - listen 443; - server_name ___my.example.com___ www.___my.example.com___; - - ssl on; - ssl_certificate /etc/letsencrypt/live/___my.example.com___/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/___my.example.com___/privkey.pem; - ssl_session_timeout 5m; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; - ssl_prefer_server_ciphers on; - - ssl_session_cache shared:SSL:10m; - ssl_dhparam /etc/ssl/private/dhparams.pem; - - location /.well-known/acme-challenge { - resolver ___NAMESERVER___; - set $certbot_addr_port certbot:443; - proxy_pass http://$certbot_addr_port; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto https; - } +# location /.well-known/acme-challenge { +# resolver ___NAMESERVER___; +# set $certbot_addr_port certbot:443; +# proxy_pass http://$certbot_addr_port; +# proxy_set_header Host $host; +# proxy_set_header X-Forwarded-For $remote_addr; +# proxy_set_header X-Forwarded-Proto https; +# } location / { # checks for static file, if not found proxy to app diff --git a/compose/nginx/start.sh b/compose/nginx/start.sh index fa40ed9..b7e248a 100755 --- a/compose/nginx/start.sh +++ b/compose/nginx/start.sh @@ -22,26 +22,26 @@ fi # This bit waits until the letsencrypt container has done its thing. # We see the changes here bceause there's a docker volume mapped. -echo Waiting for folder /etc/letsencrypt/live/$MY_DOMAIN_NAME to exist -while [ ! -d /etc/letsencrypt/live/$MY_DOMAIN_NAME ] ; -do - sleep 2 -done +#echo Waiting for folder /etc/letsencrypt/live/$MY_DOMAIN_NAME to exist +#while [ ! -d /etc/letsencrypt/live/$MY_DOMAIN_NAME ] ; +#do +# sleep 2 +#done -while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/fullchain.pem ] ; -do - echo Waiting for file fullchain.pem to exist - sleep 2 -done +#while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/fullchain.pem ] ; +#do +# echo Waiting for file fullchain.pem to exist +# sleep 2 +#done -while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/privkey.pem ] ; -do - echo Waiting for file privkey.pem to exist - sleep 2 -done +#while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/privkey.pem ] ; +#do +# echo Waiting for file privkey.pem to exist +# sleep 2 +#done # This is added so that when the certificate is being renewed or is already in place, nginx waits for everything to be good. -sleep 15 +#sleep 15 echo replacing ___my.example.com___/$MY_DOMAIN_NAME diff --git a/config/settings/production.py b/config/settings/production.py index 5ab3a21..d58fe64 100644 --- a/config/settings/production.py +++ b/config/settings/production.py @@ -25,7 +25,7 @@ SECRET_KEY = env('DJANGO_SECRET_KEY') # This ensures that Django will be able to detect a secure connection # properly on Heroku. -SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # Use Whitenoise to serve static files # See: https://whitenoise.readthedocs.io/ WHITENOISE_MIDDLEWARE = ('whitenoise.middleware.WhiteNoiseMiddleware', ) @@ -46,7 +46,7 @@ SECURE_CONTENT_TYPE_NOSNIFF = env.bool( SECURE_BROWSER_XSS_FILTER = True SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True -SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) +#SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) CSRF_COOKIE_SECURE = True CSRF_COOKIE_HTTPONLY = True X_FRAME_OPTIONS = 'DENY' @@ -56,7 +56,7 @@ X_FRAME_OPTIONS = 'DENY' # Hosts/domain names that are valid for this site # See https://docs.djangoproject.com/en/1.6/ref/settings/#allowed-hosts ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['maidstone-hackspace.org.uk']) -ALLOWED_HOSTS.append('172.*') +ALLOWED_HOSTS.append('127.0.0.1') ALLOWED_HOSTS.append('172.18.0.5') # END SITE CONFIGURATION diff --git a/config/settings/stage.py b/config/settings/stage.py new file mode 100644 index 0000000..73feb9d --- /dev/null +++ b/config/settings/stage.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +""" +Production Configurations + +- Use Amazon's S3 for storing static files and uploaded media +- Use mailgun to send emails +- Use Redis for cache + + +""" +from __future__ import absolute_import, unicode_literals + +from boto.s3.connection import OrdinaryCallingFormat +from django.utils import six + + +from .common import * # noqa + +# SECRET CONFIGURATION +# ------------------------------------------------------------------------------ +# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key +# Raises ImproperlyConfigured exception if DJANGO_SECRET_KEY not in os.environ +SECRET_KEY = env('DJANGO_SECRET_KEY') + + +# This ensures that Django will be able to detect a secure connection +# properly on Heroku. +# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +# Use Whitenoise to serve static files +# See: https://whitenoise.readthedocs.io/ +WHITENOISE_MIDDLEWARE = ('whitenoise.middleware.WhiteNoiseMiddleware', ) +MIDDLEWARE = WHITENOISE_MIDDLEWARE + MIDDLEWARE + + +# SECURITY CONFIGURATION +# ------------------------------------------------------------------------------ +# See https://docs.djangoproject.com/en/1.9/ref/middleware/#module-django.middleware.security +# and https://docs.djangoproject.com/ja/1.9/howto/deployment/checklist/#run-manage-py-check-deploy + +# set this to 60 seconds and then to 518400 when you can prove it works +SECURE_HSTS_SECONDS = 60 +SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool( + 'DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', default=True) +SECURE_CONTENT_TYPE_NOSNIFF = env.bool( + 'DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True) +SECURE_BROWSER_XSS_FILTER = True +SESSION_COOKIE_SECURE = True +SESSION_COOKIE_HTTPONLY = True +#SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) +CSRF_COOKIE_SECURE = True +CSRF_COOKIE_HTTPONLY = True +X_FRAME_OPTIONS = 'DENY' + +# SITE CONFIGURATION +# ------------------------------------------------------------------------------ +# Hosts/domain names that are valid for this site +# See https://docs.djangoproject.com/en/1.6/ref/settings/#allowed-hosts +ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['test.maidstone-hackspace.org.uk']) +ALLOWED_HOSTS.append('127.0.0.1') +ALLOWED_HOSTS.append('172.18.0.5') + +# END SITE CONFIGURATION + +INSTALLED_APPS += ('gunicorn', ) + + +# STORAGE CONFIGURATION +# ------------------------------------------------------------------------------ +# Uploaded Media Files +# ------------------------ +# See: http://django-storages.readthedocs.io/en/latest/index.html +INSTALLED_APPS += ( + 'storages', +) + +AWS_ACCESS_KEY_ID = env('DJANGO_AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY = env('DJANGO_AWS_SECRET_ACCESS_KEY') +AWS_STORAGE_BUCKET_NAME = env('DJANGO_AWS_STORAGE_BUCKET_NAME') +AWS_AUTO_CREATE_BUCKET = True +AWS_QUERYSTRING_AUTH = False +AWS_S3_CALLING_FORMAT = OrdinaryCallingFormat() + +# AWS cache settings, don't change unless you know what you're doing: +AWS_EXPIRY = 60 * 60 * 24 * 7 + +# TODO See: https://github.com/jschneier/django-storages/issues/47 +# Revert the following and use str after the above-mentioned bug is fixed in +# either django-storage-redux or boto +AWS_HEADERS = { + 'Cache-Control': six.b('max-age=%d, s-maxage=%d, must-revalidate' % ( + AWS_EXPIRY, AWS_EXPIRY)) +} + +# URL that handles the media served from MEDIA_ROOT, used for managing +# stored files. +MEDIA_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME + + +# Static Assets +# ------------------------ +STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' + +# COMPRESSOR +# ------------------------------------------------------------------------------ +COMPRESS_STORAGE = 'storages.backends.s3boto.S3BotoStorage' +COMPRESS_URL = STATIC_URL +COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', default=True) +# EMAIL +# ------------------------------------------------------------------------------ +DEFAULT_FROM_EMAIL = env('DJANGO_DEFAULT_FROM_EMAIL', + default='Maidstone Hackspace ') +EMAIL_SUBJECT_PREFIX = env('DJANGO_EMAIL_SUBJECT_PREFIX', default='[Maidstone Hackspace] ') +SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_EMAIL) +SERVER_EMAIL_PORT = '465' + +EMAIL_USE_TLS = False +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_HOST_PASSWORD = env('EMAIL_PASSWORD') +EMAIL_HOST_USER = env('EMAIL_USER') +EMAIL_PORT = 465 + +# Anymail with Mailgun +#INSTALLED_APPS += ("anymail", ) +#ANYMAIL = { +# "MAILGUN_API_KEY": env('DJANGO_MAILGUN_API_KEY'), +# "MAILGUN_SENDER_DOMAIN": env('MAILGUN_SENDER_DOMAIN') +#} +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" + +# TEMPLATE CONFIGURATION +# ------------------------------------------------------------------------------ +# See: +# https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.loaders.cached.Loader +TEMPLATES[0]['OPTIONS']['loaders'] = [ + ('django.template.loaders.cached.Loader', [ + 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ]), +] + +# DATABASE CONFIGURATION +# ------------------------------------------------------------------------------ + +# Use the Heroku-style specification +# Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ +DATABASES['default'] = env.db('DATABASE_URL') + +# CACHING +# ------------------------------------------------------------------------------ + +REDIS_LOCATION = '{0}/{1}'.format(env('REDIS_URL', default='redis://127.0.0.1:6379'), 0) +# Heroku URL does not pass the DB number, so we parse it in +CACHES = { + 'default': { + 'BACKEND': 'django_redis.cache.RedisCache', + 'LOCATION': REDIS_LOCATION, + 'OPTIONS': { + 'CLIENT_CLASS': 'django_redis.client.DefaultClient', + 'IGNORE_EXCEPTIONS': True, # mimics memcache behavior. + # http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior + } + } +} + + +# LOGGING CONFIGURATION +# ------------------------------------------------------------------------------ +# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error when DEBUG=False. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s ' + '%(process)d %(thread)d %(message)s' + }, + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + }, + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + }, + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True + }, + 'django.security.DisallowedHost': { + 'level': 'ERROR', + 'handlers': ['console', 'mail_admins'], + 'propagate': True + } + } +} + +# Custom Admin URL, use {% url 'admin:index' %} +ADMIN_URL = env('DJANGO_ADMIN_URL') + +# Your production stuff: Below this line define 3rd party library settings +# ------------------------------------------------------------------------------ diff --git a/docker-compose.yml b/docker-compose.yml index 99d65fc..891b98a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,7 +48,7 @@ services: environment: - MY_DOMAIN_NAME=maidstone-hackspace.org.uk ports: - - "0.0.0.0:80:80" + - "0.0.0.0:8080:80" volumes: - .:/app - gunicorn_socket:/data/sockets diff --git a/mhackspace/contact/forms.py b/mhackspace/contact/forms.py index 855c97d..179dac7 100644 --- a/mhackspace/contact/forms.py +++ b/mhackspace/contact/forms.py @@ -14,7 +14,7 @@ class ContactForm(forms.Form): required=True, widget=forms.Textarea ) - type = forms.MultipleChoiceField( + enquiry_type = forms.ChoiceField( required=True, widget=forms.Select, choices=TYPES) diff --git a/mhackspace/contact/views.py b/mhackspace/contact/views.py index 917c7e7..8924b3b 100644 --- a/mhackspace/contact/views.py +++ b/mhackspace/contact/views.py @@ -1,10 +1,25 @@ from django.shortcuts import render +from django.core.mail import EmailMessage +from django.contrib import messages from mhackspace.contact.forms import ContactForm # add to your views def contact(request): form_class = ContactForm - + form = form_class(data=request.POST) + if request.method == 'POST': + form = form_class(data=request.POST) + if form.is_valid(): + data = form.cleaned_data + email = EmailMessage( + '[%s] - %s' % (data['enquiry_type'], data['subject']), + data['message'], + to=['no-reply@maidstone-hackspace..org.uk']) + email.send() + messages.add_message(request, messages.INFO, 'E-Mail sent') + + return render(request, 'pages/contact.html', { - 'form': form_class, + 'form': form, }) + diff --git a/mhackspace/templates/base.html b/mhackspace/templates/base.html index aa5764b..c2a2907 100644 --- a/mhackspace/templates/base.html +++ b/mhackspace/templates/base.html @@ -1,4 +1,5 @@ -{% load staticfiles i18n compress %} +{% load i18n compress %} +{% load static from staticfiles %} @@ -99,14 +100,14 @@ Members
diff --git a/mhackspace/templates/pages/contact.html b/mhackspace/templates/pages/contact.html index cf44ea5..f03d8c0 100644 --- a/mhackspace/templates/pages/contact.html +++ b/mhackspace/templates/pages/contact.html @@ -6,7 +6,13 @@ {% block content %}

Contact us

-
+ Please fill in details below, and we will get back to you when possible, alternatively try our live chat.
+ + Click here to chat with us + + + + {% csrf_token %} {{ form|crispy }} {{ google_capture }} diff --git a/mhackspace/templates/pages/home.html b/mhackspace/templates/pages/home.html index 61c4caf..9dea5f0 100644 --- a/mhackspace/templates/pages/home.html +++ b/mhackspace/templates/pages/home.html @@ -5,6 +5,5 @@

Introduction

Hackspaces are a shared space where artists, designers, makers, hackers, programmers, tinkerers, professionals and hobbyists can work on their projects, share knowledge and collaborate.We are in the process of developing Maidstone Hackspace. We're previous members of (ICMP) and looking to form a new space in the future. At the moment, communication is via google groups, email, and the website. If you're at all intrested please join our mailing list and make yourself known! - Click here to chat with us https://hangouts.google.com/group/oDcAL0nDfQYfO3qq1 {% show_feeds %} {% endblock content %} diff --git a/stage.yml b/stage.yml new file mode 100644 index 0000000..4b7ddf6 --- /dev/null +++ b/stage.yml @@ -0,0 +1,77 @@ +version: '2' + +volumes: + sockets: + driver: local + external: true + postgres_data: + driver: local + postgres_backup: + driver: local + + +# volumes: +# sockets: +# driver: local +# data: +# driver: local + + +services: + postgres: + build: ./compose/postgres + volumes: + - postgres_data:/var/lib/postgresql/data + - postgres_backup:/backups + env_file: .env + + django: + build: + context: . + dockerfile: ./compose/django/Dockerfile + user: django + depends_on: + - postgres + - redis + command: /gunicorn.sh + env_file: .env + volumes: + - .:/app + - sockets:/data/sockets + +# nginx: +# build: ./compose/nginx +# env_file: .env +# depends_on: +# - django +# - certbot +# +# environment: +# - MY_DOMAIN_NAME=test.maidstone-hackspace.org.uk +# ports: +# - "0.0.0.0:8080:80" +# volumes: +# - .:/app +# - sockets:/data/sockets +# - "0.0.0.0:443:443" +# volumes: +# - /etc/letsencrypt:/etc/letsencrypt +# - /var/lib/letsencrypt:/var/lib/letsencrypt + +# certbot: +# image: quay.io/letsencrypt/letsencrypt +# command: bash -c "sleep 6 && certbot certonly -n --standalone -d maidstone-hackspace.org.uk --text --agree-tos --email support@maidstone-hackspace.org.uk --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01" +# entrypoint: "" +# volumes: +# - /etc/letsencrypt:/etc/letsencrypt +# - /var/lib/letsencrypt:/var/lib/letsencrypt +# ports: +# - "80" +# - "443" +# environment: +# - TERM=xterm + + + redis: + image: redis:latest + diff --git a/staticfiles/staticfiles b/staticfiles/staticfiles old mode 100644 new mode 100755