diff --git a/.dockerignore b/.dockerignore new file mode 100755 index 00000000..19e01807 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +db/ diff --git a/.github/workflows/build-standalone-docker-image.yml b/.github/workflows/build-standalone-docker-image.yml new file mode 100644 index 00000000..1a013de5 --- /dev/null +++ b/.github/workflows/build-standalone-docker-image.yml @@ -0,0 +1,39 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - main + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + run: echo '${{ secrets.DOCKER_HUB_PASS }}' | docker login -u djangohelpdesk --password-stdin + + - name: Build Docker image + run: docker build --file standalone/Dockerfile -t djangohelpdesk/standalone:latest .. + + - name: Build extras Docker image + run: docker build --file standalone/Dockerfile.extras -t djangohelpdesk/standalone-extras:latest .. + + - name: Push Docker image + run: docker push djangohelpdesk/standalone:latest + + - name: Push extras Docker image + run: docker push djangohelpdesk/standalone-extras:latest + + - name: Tag and push Docker image with year, month and Git SHA + run: docker tag djangohelpdesk/standalone:latest djangohelpdesk/standalone:$(date +%Y-%m)-$(git rev-parse --short HEAD) ; docker push djangohelpdesk/standalone:$(date +%Y-%m)-$(git rev-parse --short HEAD) + + - name: Tag and push extras Docker image with year, month and Git SHA + run: docker tag djangohelpdesk/standalone-extras:latest djangohelpdesk/standalone-extras:$(date +%Y-%m)-$(git rev-parse --short HEAD) ; docker push djangohelpdesk/standalone-extras:$(date +%Y-%m)-$(git rev-parse --short HEAD) + + - name: If we are at a tag, add a tag to the current docker image push the image with the tag + if: startsWith(github.ref, 'refs/tags/') + run: docker tag djangohelpdesk/standalone:latest djangohelpdesk/standalone:${{ github.ref }} ; docker push djangohelpdesk/standalone:${{ github.ref }} diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 09fd02ca..a174ab2f 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -11,14 +11,14 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11"] django-version: ["32","4"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-testing.txt')}}-${{ hashFiles('tox.ini') }}-${{ matrix.python-version }}-${{ matrix.django-version }} diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 55ab90ff..00000000 --- a/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -ARG PYTHON_VERSION=3.11.5-slim-bookworm - -# define an alias for the specfic python version used in this file. -FROM python:${PYTHON_VERSION} as python - -FROM python as python-build-stage - -# Install apt packages -RUN apt-get update && apt-get install --no-install-recommends -y \ - # dependencies for building Python packages - build-essential - -# Requirements are installed here to ensure they will be cached. -COPY ./requirements.txt ./requirements-dev.txt / - -# Create Python Dependency and Sub-Dependency Wheels -RUN pip install packaging -RUN pip wheel --wheel-dir /usr/src/app/wheels \ - -r requirements.txt \ - -r requirements-dev.txt - -FROM python as python-run-stage - -ARG APP_HOME=/app - -ENV PYTHONUNBUFFERED 1 -ENV PYTHONDONTWRITEBYTECODE 1 - -WORKDIR ${APP_HOME} - -COPY --from=python-build-stage /usr/src/app/wheels /wheels/ - -# use wheels to install python dependencies -RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ - && rm -rf /wheels/ - -COPY ./entrypoint /entrypoint -RUN sed -i 's/\r$//g' /entrypoint && chmod +x /entrypoint - -FROM python-run-stage AS backend - -# copy application code to WORKDIR -COPY . ${APP_HOME} - -ENTRYPOINT ["/entrypoint"] -CMD ["python3", "demo/manage.py", "runserver", "--noreload", "0.0.0.0:8080"] diff --git a/docs/index.rst b/docs/index.rst index 7f1ffe04..1b5fe292 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ Contents :maxdepth: 2 :glob: + standalone install upgrade configuration diff --git a/docs/install.rst b/docs/install.rst index f486defc..c28fefaf 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,4 +1,4 @@ -Installation +Installation/integration into an existing Django application ============ .. note:: diff --git a/docs/standalone.rst b/docs/standalone.rst index 3d1e14b8..4d3a3318 100644 --- a/docs/standalone.rst +++ b/docs/standalone.rst @@ -1,7 +1,11 @@ -Django-Helpdesk Standalone Installation +Standalone Installation ======================================= -Installation +You can find standalone docker images at `djangohelpdesk/standalone:latest `_. + +You will also find an alternative `standalone-extras `_ image with extra libraries needed to use the standalone image on cloud platforms such as AWS. You can find a full list of extra packages included in the extra's image `here `_. + +Installation using docker-compose ------------ 1. Clone the django-helpdesk repository: @@ -28,6 +32,7 @@ Installation docker-compose up + Creating an Admin User ---------------------- @@ -62,11 +67,9 @@ Configuration for Production Use 1. Update the `Caddyfile` to replace the `localhost` URL with your desired production URL. -2. Modify the `docker-compose` file to adjust the paths. By default, files are stored in `/tmp`. +2. For custom configurations, bindmount a `local_settings.py` into `/opt/django-helpdesk/standalone/config/local_settings.py`. -3. For custom configurations, bindmount a `local_settings.py` into `/opt/django-helpdesk/standalone/config/local_settings.py`. - -4. To customize the logo in the top-left corner of the helpdesk: +3. To customize the logo in the top-left corner of the helpdesk: .. code-block:: html @@ -81,9 +84,69 @@ Configuration for Production Use } +# Environment Variables Reference + +## Database Configuration +| Variable | Default | Description | +|----------|---------|-------------| +| ```POSTGRES_DB``` | ```postgres``` | Database name | +| ```POSTGRES_USER``` | ```postgres``` | Database user | +| ```POSTGRES_PASSWORD``` | ```postgres``` | Database password | +| ```POSTGRES_HOST``` | ```postgres``` | Database host | +| ```POSTGRES_PORT``` | ```5432``` | Database port | + +## Email Configuration +| Variable | Default | Description | +|----------|---------|-------------| +| ```DEFAULT_FROM_EMAIL``` | ```example@example.com``` | Default sender email address | +| ```SERVER_EMAIL``` | ```example@example.com``` | Server email address | +| ```EMAIL_HOST``` | *Required* | SMTP server host | +| ```EMAIL_PORT``` | *Required* | SMTP server port | + +## Static Files +| Variable | Default | Description | +|----------|---------|-------------| +| ```DJANGO_HELPDESK_STATIC_ROOT``` | ```./static``` | Static files root directory | +| ```DJANGO_HELPDESK_STATIC_URL``` | ```/static/``` | Static files URL prefix | + +## Security Settings +| Variable | Default | Description | +|----------|---------|-------------| +| ```DJANGO_HELPDESK_SECRET_KEY``` | *Required* | Django secret key | +| ```DJANGO_HELPDESK_ALLOWED_HOSTS``` | ```*, localhost, 0.0.0.0``` | Comma-separated list of allowed hosts | + +## Helpdesk Core Settings +| Variable | Default | Description | +|----------|---------|-------------| +| ```HELPDESK_USE_EMAIL_AS_SUBMITTER``` | ```True``` | Use email as ticket submitter | +| ```HELPDESK_EMAIL_ON_TICKET_ASSIGN``` | ```True``` | Send email on ticket assignment | +| ```HELPDESK_EMAIL_ON_TICKET_CHANGE``` | ```True``` | Send email on ticket changes | +| ```HELPDESK_LOGIN_VIEW_TICKETLIST``` | ```True``` | Show ticket list after login | +| ```HELPDESK_PRESET_REPLIES``` | ```True``` | Enable preset replies | +| ```HELPDESK_TICKETS_PER_PAGE``` | ```25``` | Number of tickets per page | + +## Public Portal Settings +| Variable | Default | Description | +|----------|---------|-------------| +| ```HELPDESK_PUBLIC_ENABLED``` | ```True``` | Enable public web portal | +| ```HELPDESK_VIEW_A_TICKET_PUBLIC``` | ```True``` | Allow public ticket viewing | +| ```HELPDESK_SUBMIT_A_TICKET_PUBLIC``` | ```True``` | Allow public ticket submission | +| ```HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT``` | ```False``` | Redirect to login instead of public portal | + +## Feature Toggles +| Variable | Default | Description | +|----------|---------|-------------| +| ```HELPDESK_KB_ENABLED``` | ```True``` | Enable knowledge base | +| ```HELPDESK_TICKETS_TIMELINE_ENABLED``` | ```True``` | Enable ticket timeline | +| ```HELPDESK_SHOW_CHANGE_PASSWORD``` | ```True``` | Allow users to change passwords | + + + AWS SES Email Configuration --------------------------- +You will need to use the standalone-extras image for SES support. + An example `local_settings` configuration for utilizing AWS SES for email: .. code-block:: python @@ -99,18 +162,14 @@ An example `local_settings` configuration for utilizing AWS SES for email: AWS_SES_REGION_ENDPOINT = "email.eu-west-1.amazonaws.com" AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY") -To integrate `django-ses`, bindmount a file to `/opt/extra-dependencies.txt` containing: - -.. code-block:: text - - django-ses - Make sure you update the `docker.env` file with the necessary secrets. S3 base attachment support --------------------------- +You will need to use the standalone-extras image for S3 support. + Working from the previous SES example we add the following to `local_settings`: .. code-block:: python @@ -124,10 +183,3 @@ Working from the previous SES example we add the following to `local_settings`: AWS_DEFAULT_ACL = "private" DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" - -To integrate `django-ses`, bindmount a file to `/opt/extra-dependencies.txt` containing: - -.. code-block:: text - - django-storages - boto3 diff --git a/entrypoint b/entrypoint deleted file mode 100644 index 28007576..00000000 --- a/entrypoint +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o pipefail -set -o nounset - -pip3 install -e . --user || pip3 install -e . -pip3 install -e demo --user || pip3 install -e demo -python3 demo/manage.py migrate --noinput - -if [ ! -e "/app/demo/demodesk/db.sqlite3" ]; then - DJANGO_SUPERUSER_PASSWORD=Test1234 python3 demo/manage.py createsuperuser --username admin --email helpdesk@example.com --noinput - # Install fixtures - python3 demo/manage.py loaddata emailtemplate.json - python3 demo/manage.py loaddata demo.json -fi - -exec "$@" diff --git a/helpdesk/templates/helpdesk/my_tickets.html b/helpdesk/templates/helpdesk/my_tickets.html index c411703b..d8cbdd8d 100644 --- a/helpdesk/templates/helpdesk/my_tickets.html +++ b/helpdesk/templates/helpdesk/my_tickets.html @@ -35,7 +35,7 @@ window.addEventListener('load', function() $.get(endpoint, function(data) { $('#ticketsTable tbody').empty(); data.results.forEach(function(ticket) { - ticket.title = $('div').text(ticket.title).html(); + ticket.title = $('
').html(ticket.title).html(); $('#ticketsTable tbody').append(` diff --git a/helpdesk/update_ticket.py b/helpdesk/update_ticket.py index 8577472b..51ac7d19 100644 --- a/helpdesk/update_ticket.py +++ b/helpdesk/update_ticket.py @@ -198,7 +198,6 @@ def update_ticket( files=None, public=False, owner=-1, - ticket_title=None, priority=-1, queue=-1, new_status=None, @@ -268,13 +267,13 @@ def update_ticket( files = process_attachments(f, files) if files else [] - if ticket_title and ticket_title != ticket.title: + if title and title != ticket.title: c = f.ticketchange_set.create( field=_('Title'), old_value=ticket.title, - new_value=ticket_title, + new_value=title, ) - ticket.title = ticket_title + ticket.title = title if new_status != old_status: c = f.ticketchange_set.create( diff --git a/standalone/.dockerignore b/standalone/.dockerignore new file mode 100755 index 00000000..19e01807 --- /dev/null +++ b/standalone/.dockerignore @@ -0,0 +1 @@ +db/ diff --git a/standalone/Dockerfile b/standalone/Dockerfile index 002a5830..d67bb73d 100644 --- a/standalone/Dockerfile +++ b/standalone/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-slim-bullseye +FROM python:3.11-slim-bullseye LABEL src=https://github.com/django-helpdesk/django-helpdesk RUN apt-get update RUN apt-get install -yqq \ @@ -6,11 +6,9 @@ RUN apt-get install -yqq \ postgresql-client \ cron \ git -COPY requirements.txt /opt/django-helpdesk/requirements.txt -COPY standalone/extra-requirements.txt /opt/django-helpdesk/standalone/extra-requirements.txt +COPY . /opt/ RUN pip3 install -r /opt/django-helpdesk/requirements.txt -RUN pip3 install -r /opt/django-helpdesk/standalone/extra-requirements.txt -COPY . /opt/django-helpdesk +RUN pip3 install -r /opt/django-helpdesk/standalone/requirements.txt WORKDIR /opt/django-helpdesk RUN pip3 install -e . RUN DJANGO_HELPDESK_SECRET_KEY=foo python3 standalone/manage.py collectstatic diff --git a/standalone/Dockerfile.extras b/standalone/Dockerfile.extras new file mode 100644 index 00000000..861ec54c --- /dev/null +++ b/standalone/Dockerfile.extras @@ -0,0 +1,2 @@ +FROM djangohelpdesk/standalone +RUN pip3 install -r /opt/django-helpdesk/standalone/requirements-extras.txt \ No newline at end of file diff --git a/standalone/README.md b/standalone/README.md index 7b68fbe7..3421ea75 100644 --- a/standalone/README.md +++ b/standalone/README.md @@ -5,3 +5,5 @@ This is a standalone installation of Django-helpdesk allowing you to run django- For installation instructions see [the documentation](../docs/standalone.rst) +For development and testing purposes, the `docker-compose-dev.yml` file will build the standalone docker images from source. + diff --git a/standalone/config/settings.py b/standalone/config/settings.py index 643caebf..b658baaa 100644 --- a/standalone/config/settings.py +++ b/standalone/config/settings.py @@ -98,7 +98,6 @@ HELPDESK_DEFAULT_SETTINGS = { 'email_on_ticket_assign': os.environ.get('HELPDESK_EMAIL_ON_TICKET_ASSIGN', 'True') == 'True', 'email_on_ticket_change': os.environ.get('HELPDESK_EMAIL_ON_TICKET_CHANGE', 'True') == 'True', 'login_view_ticketlist': os.environ.get('HELPDESK_LOGIN_VIEW_TICKETLIST', 'True') == 'True', - 'email_on_ticket_apichange': os.environ.get('HELPDESK_EMAIL_ON_TICKET_APICHANGE', 'True') == 'True', 'preset_replies': os.environ.get('HELPDESK_PRESET_REPLIES', 'True') == 'True', 'tickets_per_page': os.environ.get('HELPDESK_TICKETS_PER_PAGE', '25'), } diff --git a/standalone/docker-compose-dev.yml b/standalone/docker-compose-dev.yml new file mode 100644 index 00000000..a42ae12a --- /dev/null +++ b/standalone/docker-compose-dev.yml @@ -0,0 +1,30 @@ +version: '2' +services: + caddy: + image: caddy:2 + restart: unless-stopped + volumes: + - /tmp/data/caddy/data:/data + - /tmp/data/caddy/config:/config + - ./Caddyfile:/etc/caddy/Caddyfile:r + ports: + - "80:80" + - "443:443" + + django-helpdesk: + build: + context: .. + dockerfile: standalone/Dockerfile + user: root + volumes: + - /tmp/django-helpdesk-data:/data/ + - ../:/opt/django-helpdesk + env_file: docker.env + depends_on: + - postgres + + postgres: + image: postgres:12-bullseye + volumes: + - ./db:/var/lib/postgresql/data + env_file: docker.env diff --git a/standalone/docker-compose.yml b/standalone/docker-compose.yml index 5e35f3dd..d797cc7c 100644 --- a/standalone/docker-compose.yml +++ b/standalone/docker-compose.yml @@ -1,30 +1,36 @@ version: '2' + +# Define named volumes +volumes: + caddy_data: + caddy_config: + helpdesk_data: + postgres_data: + services: - caddy: - image: caddy:2 - restart: unless-stopped - volumes: - - /tmp/data/caddy/data:/data - - /tmp/data/caddy/config:/config - - ./Caddyfile:/etc/caddy/Caddyfile:r - ports: - - "80:80" - - "443:443" + caddy: + image: caddy:2 + restart: unless-stopped + volumes: + - caddy_data:/data + - caddy_config:/config + - ./Caddyfile:/etc/caddy/Caddyfile:ro + ports: + - "80:80" + - "443:443" - django-helpdesk: - build: - context: .. - dockerfile: standalone/Dockerfile - user: root - volumes: - - /tmp/django-helpdesk-data:/data/ - - ./custom_navigation_header.html:/opt/django-helpdesk/helpdesk/templates/helpdesk/custom_navigation_header.html:r - env_file: docker.env - depends_on: - - postgres + django-helpdesk: + image: djangohelpdesk/standalone + user: root + volumes: + - helpdesk_data:/data + - ./custom_navigation_header.html:/opt/django-helpdesk/helpdesk/templates/helpdesk/custom_navigation_header.html:ro + env_file: docker.env + depends_on: + - postgres - postgres: - image: postgres:12-bullseye - volumes: - - ./db:/var/lib/postgresql/data - env_file: docker.env + postgres: + image: postgres:12-bullseye + volumes: + - postgres_data:/var/lib/postgresql/data + env_file: docker.env diff --git a/standalone/requirements-extras.txt b/standalone/requirements-extras.txt new file mode 100644 index 00000000..d3d06990 --- /dev/null +++ b/standalone/requirements-extras.txt @@ -0,0 +1,4 @@ +django-ses +django-storages +boto3 +git+https://github.com/KristobalJunta/django-keycloak.git@1c3b555335e5a95f1bf1281f19ed082d96032f80 diff --git a/standalone/extra-requirements.txt b/standalone/requirements.txt similarity index 100% rename from standalone/extra-requirements.txt rename to standalone/requirements.txt