diff --git a/.env b/.env new file mode 100644 index 0000000..8f1f5af --- /dev/null +++ b/.env @@ -0,0 +1,10 @@ +POSTGRES_DB=mememaker +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +DB_HOST=db +DB_PORT=5432 + +DEBUG=1 +SECRET_KEY=foo +STATIC_PATH=/vol/web/static +MEDIA_PATH=/vol/web/media diff --git a/djangoproject/Dockerfile b/djangoproject/Dockerfile new file mode 100644 index 0000000..ea0251d --- /dev/null +++ b/djangoproject/Dockerfile @@ -0,0 +1,65 @@ +# FROM python:3.8.5-buster + +# LABEL key="MemesBD" + +# ADD . /api +# WORKDIR /api +# # You will need this if you need PostgreSQL, otherwise just skip this +# # RUN apk update && apk add postgresql-dev gcc python3-dev musl-dev libffi-dev +# # RUN pip install uwsgi +# RUN pip install -r requirements.txt + +# RUN sh initdb.sh +# # RUN python manage.py collectstatic + +# ENV PORT=8000 +# EXPOSE 8000 +# # Runner script here +# # CMD ["/api/runner.sh"] +# # CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mememaker.wsgi:application"] +# CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"] + + +# # docker build -t mememaker -f Dockerfile . +# # docker run -it -p 80:8000 mememaker + + +FROM subangkar/drf-mememaker:latest + +ENV PYTHONDONTWRITEBYTECODE 1 # Prevents Python from writing pyc files to disc +ENV PYTHONUNBUFFERED 1 # Prevents Python from buffering stdout and stderr + +ENV PATH="/scripts:${PATH}" +ENV CONTAINER=1 + +ENV STATIC_PATH=/vol/web/static +ENV MEDIA_PATH=/vol/web/media + + +# RUN mkdir /app +ADD djangoproject /app +WORKDIR /app + + +COPY scripts /scripts +RUN chmod +x /scripts/* + + +COPY .env /app/ +COPY djangoproject /app/ + + +RUN mkdir -p $MEDIA_PATH $STATIC_PATH + +RUN adduser --disabled-password user + +RUN chown -R user:user /vol && \ + chmod -R 755 /vol/web + +#USER user + + +RUN find . -path "*/migrations/*.py" -not -name "__init__.py" -delete && \ + find . -path "*/migrations/*.pyc" -delete + +CMD ["entrypoint.sh"] diff --git a/djangoproject/coreapp/urls.py b/djangoproject/coreapp/urls.py index f2a8bd0..1d482da 100644 --- a/djangoproject/coreapp/urls.py +++ b/djangoproject/coreapp/urls.py @@ -18,5 +18,4 @@ urlpatterns = [ path('', include(router.urls)), path('', include(post_router.urls)), - # path('auth/', include('rest_framework.urls', namespace='rest_framework')), ] diff --git a/djangoproject/mememaker/settings.py b/djangoproject/mememaker/settings.py index 4df861b..b711dc5 100644 --- a/djangoproject/mememaker/settings.py +++ b/djangoproject/mememaker/settings.py @@ -19,10 +19,10 @@ # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '3jjb4s6f!6x@l+g%oga7&k9i^ipj45x@!5*t2_bnd(-7afckw(' +SECRET_KEY = os.environ.get('SECRET_KEY', '3jjb4s6f!6x@l+g%oga7&k9i^ipj45x@!5*t2_bnd(-7afckw(') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = bool(int(os.environ.get('DEBUG', 1))) ALLOWED_HOSTS = ['*'] @@ -97,13 +97,25 @@ # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases -DATABASES = { - 'default': { +DATABASE_CONFIGS = { + 'sqlite': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + }, + 'postgres': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.environ.get('POSTGRES_DB', 'postgres'), + 'USER': os.environ.get('POSTGRES_USER', 'postgres'), + 'PASSWORD': os.environ.get('POSTGRES_PASSWORD', 'postgres'), + 'HOST': os.environ.get('DB_HOST', 'db'), + 'PORT': os.environ.get('DB_PORT', '5432'), } } +DATABASES = { + 'default': DATABASE_CONFIGS['postgres'] if bool(int(os.environ.get('CONTAINER', 0))) else DATABASE_CONFIGS['sqlite'] +} + # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators @@ -159,12 +171,17 @@ CORS_ORIGIN_REGEX_WHITELIST = [ 'http://localhost:8080', ] -STATIC_URL = '/static/' MEDIA_URL = '/media/' -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') +# MEDIA_ROOT = '/vol/web/media' if bool(int(os.environ.get('CONTAINER', 0))) else os.path.join(BASE_DIR, 'media') +MEDIA_ROOT = os.environ.get('MEDIA_PATH', os.path.join(BASE_DIR, 'media')) SITE_ID = 4 # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ STATIC_URL = '/static/' +# STATIC_ROOT = '/vol/web/static' if bool(int(os.environ.get('CONTAINER', 0))) else os.path.join(BASE_DIR, 'static') +STATIC_ROOT = os.environ.get('STATIC_PATH', os.path.join(BASE_DIR, 'static')) +# STATICFILES_DIRS = ( +# STATIC_ROOT, +# ) if bool(int(os.environ.get('CONTAINER', 0))) else () diff --git a/djangoproject/mememaker/urls.py b/djangoproject/mememaker/urls.py index b33f505..9c25424 100644 --- a/djangoproject/mememaker/urls.py +++ b/djangoproject/mememaker/urls.py @@ -50,12 +50,12 @@ url(r'^rest-auth/facebook-connect/$', FacebookConnect.as_view(), name='rest-auth-facebook-connect'), url(r'^rest-auth/google/$', GoogleLogin.as_view(), name='rest-auth-google-login'), url(r'^rest-auth/google-connect/$', GoogleConnect.as_view(), name='rest-auth-google-connect'), - # path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), + path('auth/', include('rest_framework.urls', namespace='rest_framework')), url(r'^swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), url(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), url(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), ] -if settings.DEBUG: - urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/djangoproject/requirements.txt b/djangoproject/requirements.txt index 9dbe927..0b05b38 100644 --- a/djangoproject/requirements.txt +++ b/djangoproject/requirements.txt @@ -13,7 +13,6 @@ django-extra-fields==2.0.5 django-rest-auth==0.9.5 django-sass-processor==0.8 django-sslserver==0.22 -django-tagconstants==0.1 djangorestframework==3.11.0 drf-nested-routers==0.91 drf-url-filters==0.5.1 @@ -28,6 +27,7 @@ MarkupSafe==1.1.1 oauthlib==3.1.0 packaging==20.4 Pillow==7.1.2 +psycopg2==2.8.5 pyparsing==2.4.7 python3-openid==3.1.0 pytz==2020.1 diff --git a/djangoproject/uwsgi.ini b/djangoproject/uwsgi.ini new file mode 100644 index 0000000..299fe9c --- /dev/null +++ b/djangoproject/uwsgi.ini @@ -0,0 +1,22 @@ +[uwsgi] + + +static-map = /static/=/vol/web/static +static-map = /media/=/vol/web/media + +# socket = :8000 # use socket if ngnix is on same machine as server , otherwise use http +http = :8000 # use socket if ngnix is on same machine as server , otherwise use http +module=mememaker.wsgi:application +env=DJANGO_SETTINGS_MODULE=mememaker.settings + +enable-threads +wsgi-file mememaker/wsgi.py +# process-related settings +# master +master = true +# maximum number of worker processes +processes = 10 +# the socket (use the full path to be safe +# chmod-socket = 664 +# clear environment on exit +vacuum = true \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..abace02 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,60 @@ +version: '3.7' + +volumes: + postgres_data: + static_data: + # frontend_data: + +services: + db: + image: postgres + restart: always + volumes: + - postgres_data:/var/lib/postgresql/data/ + env_file: + - .env + + web: + build: + context: . + dockerfile: djangoproject/Dockerfile + cache_from: + - subangkar/mememaker:initial + image: subangkar/mememaker:initial + volumes: + - static_data:/vol/web + ports: + - 8000:8000 # change also in uwsig ini and ngnix conf upstream + env_file: + - .env + depends_on: + - db + links: + - db:db + + + # nginx: + # build: + # context: . + # dockerfile: nginx/prod/Dockerfile + # ports: + # - 80:80 # change in ngnix conf + # volumes: + # - static_data:/vol/static + # # - frontend_data:/vol/frontend/static + # # env_file: + # # - nginx/prod/.env.dev + # depends_on: + # - web + + # vue: + # build: + # context: . + # dockerfile: frontend/Dockerfile + # env_file: + # - frontend/.env.dev + # volumes: + # - frontend_data:/app + # depends_on: + # - web + # - nginx diff --git a/nginx/prod/Dockerfile b/nginx/prod/Dockerfile new file mode 100644 index 0000000..28d3fe9 --- /dev/null +++ b/nginx/prod/Dockerfile @@ -0,0 +1,16 @@ +FROM nginx:latest + +RUN rm /etc/nginx/conf.d/default.conf + +COPY ./nginx/prod/default.conf /etc/nginx/conf.d/default.conf + +COPY ./nginx/prod/uwsgi_params /etc/nginx/uwsgi_params + +COPY ./nginx/prod/nginx.conf /etc/nginx/nginx.conf + + +USER root + +RUN mkdir -p /vol/static +RUN chmod 755 /vol/static + diff --git a/nginx/prod/default.conf b/nginx/prod/default.conf new file mode 100644 index 0000000..7aded88 --- /dev/null +++ b/nginx/prod/default.conf @@ -0,0 +1,45 @@ +server { + listen 80; + listen [::]:80; + server_name dev.mememaker.org; + + #charset koi8-r; + #access_log /var/log/nginx/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} diff --git a/nginx/prod/nginx.conf b/nginx/prod/nginx.conf new file mode 100644 index 0000000..490242f --- /dev/null +++ b/nginx/prod/nginx.conf @@ -0,0 +1,113 @@ +user nginx; +worker_processes 1; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; +events { + worker_connections 1024; +} +http { + include /etc/nginx/mime.types; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + sendfile on; + keepalive_timeout 65; + + upstream django { + server web:8000; + } + + server { + listen 80; + listen [::]:80; + server_name dev.mememaker.org; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } + } + + + server { + listen 80; + listen [::]:80; + server_name api.dev.mememaker.org; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } + } + + server{ + listen 443 ssl; + listen [::]:443 ssl; + + server_name api.dev.mememaker.org; + + server_tokens off; + + charset utf-8; + + # max upload size + client_max_body_size 75M; # adjust to taste + + location /static { + alias /vol/static; + } + location /media { + alias /vol/media; + } + location / { + uwsgi_pass django; + include /etc/nginx/uwsgi_params; + } + + ssl_certificate /etc/letsencrypt/live/api.dev.mememaker.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/api.dev.mememaker.org/privkey.pem; + } + + server { + listen 443 ssl; + listen [::]:443 ssl; + + server_name dev.mememaker.org; + + + server_tokens off; + + charset utf-8; + + # max upload size + + client_max_body_size 75M; # adjust to taste + + + location / { + root /vol/frontend/static; + index index.html; + try_files $uri $uri/ /index.html; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + ssl_certificate /etc/letsencrypt/live/api.dev.mememaker.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/api.dev.mememaker.org/privkey.pem; + } +} diff --git a/nginx/prod/uwsgi_params b/nginx/prod/uwsgi_params new file mode 100644 index 0000000..c7727cd --- /dev/null +++ b/nginx/prod/uwsgi_params @@ -0,0 +1,13 @@ +uwsgi_param QUERY_STRING $query_string; +uwsgi_param REQUEST_METHOD $request_method; +uwsgi_param CONTENT_TYPE $content_type; +uwsgi_param CONTENT_LENGTH $content_length; +uwsgi_param REQUEST_URI $request_uri; +uwsgi_param PATH_INFO $document_uri; +uwsgi_param DOCUMENT_ROOT $document_root; +uwsgi_param SERVER_PROTOCOL $server_protocol; +uwsgi_param REMOTE_ADDR $remote_addr; +uwsgi_param REMOTE_PORT $remote_port; +uwsgi_param SERVER_ADDR $server_addr; +uwsgi_param SERVER_PORT $server_port; +uwsgi_param SERVER_NAME $server_name; diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..7fa7b1b --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +python manage.py makemigrations accounts coreapp +python manage.py migrate + +python manage.py collectstatic --no-input diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh new file mode 100644 index 0000000..10f09e5 --- /dev/null +++ b/scripts/entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +python manage.py collectstatic --no-input + +python manage.py makemigrations +python manage.py migrate + +#uwsgi --http :8000 --module=mememaker.wsgi:application --env DJANGO_SETTINGS_MODULE=mememaker.settings --master --enable-threads --wsgi-file mememaker/wsgi.py + +uwsgi --ini uwsgi.ini