diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..573b7551 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,27 @@ +latex/anschreiben/veranstalter.adr +# Ignore all files in db folder +db/* +# But keep the folder +!db/.gitkeep + +*.pyc +htmlcov +pyenv +*.aux +*.log +.idea +*.pdf +*.adr +.project +.pydevproject +.settings/* +.coverage +settings_secret.py +static/* +venv/ +.vscode/* +.python-version + +node_modules/ + +*.mo \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 00000000..3fe5de79 --- /dev/null +++ b/.env @@ -0,0 +1,11 @@ +SETTINGS_FILE=settings +DEBUG=False +ALLOWED_HOSTS=.fachschaft.informatik.tu-darmstadt.de,.d120.de +SECRET_KEY= +KEYCLOAK_CLIENT_ID= +KEYCLOAK_SECRET= +KEYCLOAK_SERVER_URL= +EMAIL_HOST= +EMAIL_PORT= +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= diff --git a/.gitignore b/.gitignore index 8e614510..573b7551 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ venv/ .python-version node_modules/ + +*.mo \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..e9a86cd0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,72 @@ +## Stage 1: build stage +FROM python:3.13-slim AS builder + +RUN mkdir /app + +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +# install packages and node +RUN pip install --upgrade pip + +RUN apt-get update && apt-get install -y \ + curl \ + gnupg \ + ca-certificates \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# install pip requirements +COPY requirements.txt /app/ + +RUN pip install --no-cache-dir -r requirements.txt + +# install node_modules +COPY package*.json ./ + +RUN npm i + +## Stage 2: production stage +FROM python:3.13-slim + +RUN useradd -m -r appuser && \ + mkdir /app && \ + chown -R appuser /app + +# Copy the python dependencies and node_modules from the builder stage +COPY --from=builder /usr/local/lib/python3.13/site-packages/ /usr/local/lib/python3.13/site-packages/ +COPY --from=builder /usr/local/bin/ /usr/local/bin/ +COPY --from=builder /app/node_modules/ /app/node_modules/ + +# install gettext for translations (compiling) +RUN apt-get update && apt-get install -y gettext \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# copy the code +COPY --chown=appuser:appuser . . + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app/src + +# switch to non-root user +USER appuser + +# compile translations +RUN django-admin compilemessages + +# migrate db changes +RUN python manage.py migrate --no-input + +EXPOSE 8000 + +# start applicaiton with gunicorn +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "wsgi:application"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..b889e065 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + django-web: + build: . + container_name: pyfeedback-docker + ports: + - "8000:8000" + env_file: + - .env + volumes: + - pyfeedback-db-vol:/app/db/ + +volumes: + pyfeedback-db-vol: diff --git a/requirements.txt b/requirements.txt index a407d925..173bbe3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ django-debug-toolbar==6.0.0 freezegun==1.5.5 django-formtools==2.5.1 django-allauth[socialaccount,keycloak]==65.12.1 +gunicorn==23.0.0 diff --git a/src/django.wsgi b/src/django.wsgi deleted file mode 100644 index ac4b0cc0..00000000 --- a/src/django.wsgi +++ /dev/null @@ -1,8 +0,0 @@ -import os - -from django.core.wsgi import get_wsgi_application - - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") - -application = get_wsgi_application() diff --git a/src/locale/de/LC_MESSAGES/django.mo b/src/locale/de/LC_MESSAGES/django.mo deleted file mode 100644 index 21fa752f..00000000 Binary files a/src/locale/de/LC_MESSAGES/django.mo and /dev/null differ diff --git a/src/locale/en/LC_MESSAGES/django.mo b/src/locale/en/LC_MESSAGES/django.mo deleted file mode 100644 index aff8f190..00000000 Binary files a/src/locale/en/LC_MESSAGES/django.mo and /dev/null differ diff --git a/src/settings_production.py b/src/settings_production.py index ba6f04ea..939eb06e 100644 --- a/src/settings_production.py +++ b/src/settings_production.py @@ -1,11 +1,11 @@ from settings import * -import settings_secret as secrets +import os -DEBUG = False +DEBUG = (os.getenv("DEBUG", "False") == "True") # os.getenv only returns a string -ALLOWED_HOSTS = ['.fachschaft.informatik.tu-darmstadt.de', '.d120.de'] +ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS").split(",") -SECRET_KEY = secrets.SECRET_KEY +SECRET_KEY = os.getenv("SECRET_KEY") URL_PREFIX = 'feedback/' @@ -20,10 +20,10 @@ { "provider_id": "keycloak", "name": "Keycloak", - "client_id": secrets.KEYCLOAK_CLIENT_ID, - "secret": secrets.KEYCLOAK_SECRET, + "client_id": os.getenv("KEYCLOAK_CLIENT_ID"), + "secret": os.getenv("KEYCLOAK_SECRET"), "settings": { - "server_url": secrets.KEYCLOAK_SERVER_URL, + "server_url": os.getenv("KEYCLOAK_SERVER_URL"), }, } ] @@ -33,11 +33,11 @@ # @see https://docs.djangoproject.com/es/1.9/topics/email/ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_HOST = 'mail.d120.de' -EMAIL_PORT = 587 +EMAIL_HOST = os.getenv("EMAIL_HOST") +EMAIL_PORT = int(os.getenv("EMAIL_PORT")) EMAIL_USE_TLS = True -EMAIL_HOST_USER = 'pyfeedback' -EMAIL_HOST_PASSWORD = secrets.EMAIL_HOST_PASSWORD +EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER") +EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD") SESSION_COOKIE_PATH = '/feedback' SESSION_COOKIE_SECURE = True diff --git a/src/wsgi.py b/src/wsgi.py new file mode 100644 index 00000000..872c4101 --- /dev/null +++ b/src/wsgi.py @@ -0,0 +1,10 @@ +import os + +from django.core.wsgi import get_wsgi_application + +# select in .env which settings to use +settings_file = os.getenv("SETTINGS_FILE", "settings") + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_file) + +application = get_wsgi_application()