diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..911f982 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,12 @@ +FROM mcr.microsoft.com/devcontainers/python:1-3.11-bullseye + +ENV PYTHONUNBUFFERED 1 + +# [Optional] If your requirements rarely change, uncomment this section to add them to the image. +# COPY requirements.txt /tmp/pip-tmp/ +# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ +# && rm -rf /tmp/pip-tmp + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends diff --git a/.devcontainer/devcontainer.env b/.devcontainer/devcontainer.env new file mode 100644 index 0000000..6d60528 --- /dev/null +++ b/.devcontainer/devcontainer.env @@ -0,0 +1,9 @@ +DEBUG=0 +SECRET_KEY=dev +DJANGO_ALLOWED_HOSTS=0.0.0.0 localhost 127.0.0.1 [::1] +DB_ENGINE=django.db.backends.postgresql +DB_NAME=tally +DB_USER=postgres +DB_PASSWORD=postgres +DB_HOST=db +DB_PORT=5432 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..3eb8129 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/postgres +{ + "name": "Python 3 & PostgreSQL", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // This can be used to network with other containers or the host. + "forwardPorts": [8000, 5432, 8080], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "./.devcontainer/postCreate.sh" + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..ea1313a --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,41 @@ +version: '3.8' + +services: + app: + env_file: devcontainer.env + build: + context: .. + dockerfile: .devcontainer/Dockerfile + + volumes: + - ../..:/workspaces:cached + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + + # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + db: + image: postgres:latest + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: postgres + POSTGRES_DB: tally + POSTGRES_PASSWORD: postgres + + # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + adminer: + image: adminer + restart: unless-stopped + network_mode: service:db + +volumes: + postgres-data: diff --git a/.devcontainer/postCreate.sh b/.devcontainer/postCreate.sh new file mode 100644 index 0000000..18e1a54 --- /dev/null +++ b/.devcontainer/postCreate.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +pip install --user -r requirements.txt +cd tally +python manage.py migrate diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6d4e845 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +*.md +.venv +.vscode +.github +.devcontainer diff --git a/.vscode/launch.json b/.vscode/launch.json index 43298c2..caf336f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Python: Django", "type": "python", "request": "launch", - "program": "${workspaceFolder}\\tally\\manage.py", + "program": "${workspaceFolder}/tally/manage.py", "args": [ "runserver" ], diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..269b724 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,72 @@ +# pull official base image +FROM python:3.11.4-slim-buster as builder + +# set work directory +WORKDIR /usr/src/app + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# install system dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends gcc + +# # lint +# RUN pip install --upgrade pip +# RUN pip install flake8==6.0.0 +# COPY . /usr/src/app/ +# RUN flake8 --ignore=E501,F401 . + +# install python dependencies +COPY ./requirements.txt . +RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt + + + + +# pull official base image +FROM python:3.11.4-slim-buster + +# create directory for the app user +RUN mkdir -p /home/app + +# create the app user +RUN addgroup --system app && adduser --system --group app + +# create the appropriate directories +ENV HOME=/home/app +ENV APP_HOME=/home/app/web +RUN mkdir $APP_HOME +WORKDIR $APP_HOME + +# install dependencies +RUN apt-get update && apt-get install -y --no-install-recommends netcat +COPY --from=builder /usr/src/app/wheels /wheels +COPY --from=builder /usr/src/app/requirements.txt . +RUN pip install --upgrade pip && \ + pip install --no-cache /wheels/* + + +# copy docker-entrypoint.sh +COPY ./docker-entrypoint.sh . +RUN sed -i 's/\r$//g' $APP_HOME/docker-entrypoint.sh +RUN chmod +x $APP_HOME/docker-entrypoint.sh + +# copy project +COPY ./tally/ $APP_HOME +COPY ./prod.env ./prod.env + +RUN python manage.py collectstatic --noinput --no-post-process + + +# chown all the files to the app user +RUN chown -R app:app $APP_HOME + +# change to the app user +USER app + +# run docker-entrypoint.sh +ENTRYPOINT ["/home/app/web/docker-entrypoint.sh"] +CMD ["gunicorn", "-b=0.0.0.0:8000", "tally.wsgi:application"] +EXPOSE 8000 diff --git a/README.md b/README.md index 8710cb3..a59dbdf 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ If the backend is finished, these are the features that the server has. ## Run Locally +There is also a docker devcontainer for this project. See [devcontainer](#devcontainer). + Clone the project ```bash @@ -78,6 +80,16 @@ Superuser created successfully. You can now log in into the admin panel. +## Devcontainer + +This repository has a devcontainer that has been tested in VS Code. The devcontainer will automatically install all dependencies, start a postgres container and an adminer container. On the first build of the container it will also run the migrations. + +To be able to run the devcontainer you need to have VS Code installed with the [dev containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). + +First clone the repository and open it with VS Code. Now run the following command in the VS Code command palette: `Dev Containers: Reopen in Container`. (You can open the command palette with `Ctrl+Shift+P`). + +Once everything has started succesfully you should be able to access adminer on `localhost:8080`. To be able to run and acces tally itself, you might still need to [create an admin user](#creating-an-admin-user) (first do `cd tally`). Then you can run tally by pressing `Start Debugging (F5)` or by running the command `python manage.py runserver`. You should now be able to access tally on `localhost:8000`. If you want to run the migrations, use the command `python manage.py migrate`. + ## Environment Variables (Dummy text) Dummy text To run this project, you will need to add the following environment variables to your .env file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1d7fecc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: "3.9" + +services: + web: + build: . + ports: + - "8000:8000" + env_file: + - prod.env + depends_on: + - db + + db: + image: postgres + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + - POSTGRES_DB=tally + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + + +volumes: + postgres-data: \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..b2c5ce4 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +if [ "$1" = "gunicorn" ] +then + echo "Waiting for postgres..." + + while ! nc -z $DB_HOST $DB_PORT; do + sleep 0.1 + done + + echo "PostgreSQL started" + python ./manage.py migrate --noinput +fi + +exec "$@" diff --git a/prod.env b/prod.env new file mode 100644 index 0000000..a2f6e75 --- /dev/null +++ b/prod.env @@ -0,0 +1,9 @@ +DEBUG=0 +SECRET_KEY=change_me +DJANGO_ALLOWED_HOSTS=0.0.0.0 localhost 127.0.0.1 [::1] +DB_ENGINE=django.db.backends.postgresql +DB_NAME=tally +DB_USER=postgres +DB_PASSWORD=postgres +DB_HOST=db +DB_PORT=5432 diff --git a/requirements.txt b/requirements.txt index 8f82186..d174536 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/tally/tally/settings.py b/tally/tally/settings.py index 3c3ae65..655a12a 100644 --- a/tally/tally/settings.py +++ b/tally/tally/settings.py @@ -24,7 +24,7 @@ SECRET_KEY = 'django-insecure-q7jl@rw2!95&&oe=5!3^ygji+8x0&%9e^s^nr9@-0^5a@)gs=r' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.getenv('DEBUG', True) ALLOWED_HOSTS = [] @@ -82,10 +82,14 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + 'ENGINE': os.getenv('DB_ENGINE', 'django.db.backends.postgresql'), + 'NAME': os.getenv('DB_NAME', 'tally'), + 'USER': os.getenv('DB_USER', 'postgres'), + 'PASSWORD': os.getenv('DB_PASSWORD', 'postgres'), + 'HOST': os.getenv('DB_HOST', 'db'), + 'PORT': int(os.getenv('DB_PORT', 5432)) + } } -} # Password validation @@ -124,6 +128,12 @@ STATIC_URL = 'static/' +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = os.path.join(os.path.dirname(__file__), "../static/") + # Default primary key field type # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field diff --git a/tally/tally/urls.py b/tally/tally/urls.py index 92e5704..8dac1dd 100644 --- a/tally/tally/urls.py +++ b/tally/tally/urls.py @@ -15,13 +15,15 @@ """ from django.contrib import admin from django.urls import include, path +from django.conf.urls.static import static import products.urls as products_urls import transactions.urls as transactions_urls +import tally.settings as settings urlpatterns = [ path("admin/", admin.site.urls), path("settings/", include("dbsettings.urls")), path("products/", include((products_urls.router.urls, "products"))), path("transactions/", include((transactions_urls.router.urls, "transactions"))), -] +] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \ No newline at end of file