Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions code/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,46 @@
DJANGO_SECRET_KEY=
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
# Gunicorn Configuration
GUNICORN_PORT=8000
GUNICORN_WORKERS=2
GUNICORN_TIMEOUT=60
GUNICORN_LOG_LEVEL=info

# Django Security
DJANGO_SECRET_KEY=''
DJANGO_DEBUG=false
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost,localhost:8080
DJANGO_CSRF_TRUSTED_ORIGINS=http://127.0.0.1,http://localhost,http://localhost:8080

# Database
SQL_ENGINE=django.db.backends.sqlite3
SQL_DATABASE=db.sqlite3
DJANGO_SQLITE_DIR=/sqlite

# API Configuration
REACT_APP_API_URL=http://127.0.0.1:8080/api

# Localization
DJANGO_LANGUAGE_CODE=en-us
DJANGO_TIME_ZONE=UTC

# Email Configuration - Gmail
DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
DJANGO_EMAIL_HOST=smtp.gmail.com
DJANGO_EMAIL_PORT=587
DJANGO_EMAIL_USE_TLS=true
DJANGO_EMAIL_USE_SSL=false
DJANGO_EMAIL_HOST_USER=email@gmail.com
DJANGO_EMAIL_HOST_PASSWORD=
DJANGO_DEFAULT_FROM_EMAIL=MyMedic <email@gmail.com>

# Default Email Settings
DJANGO_SERVER_EMAIL=email@gmail.com

# Project Configuration
PROJECT_NAME=mymedic

# Admin Configuration
DJANGO_ADMIN_NAME=Admin
DJANGO_ADMIN_EMAIL=email@gmail.com
DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@admin.com
DJANGO_SUPERUSER_PASSWORD=admin
76 changes: 18 additions & 58 deletions code/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,66 +1,26 @@
FROM python:3.13-alpine AS base
FROM python:3.13-alpine

FROM base AS builder
RUN apk update \
&& apk add --no-cache \
gcc musl-dev python3-dev libffi-dev openssl-dev \
nginx su-exec

RUN apk update && apk --no-cache add python3-dev libpq-dev && mkdir /install
WORKDIR /install
COPY requirements.txt ./
RUN pip install --no-cache-dir --prefix=/install -r ./requirements.txt
RUN mkdir -p /usr/src/mymedic /sqlite /var/run/nginx
WORKDIR /usr/src/mymedic

FROM base
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

ARG USER=user
ARG USER_UID=1001
ARG PROJECT_NAME=mymedic
ARG GUNICORN_PORT=8000
ARG GUNICORN_WORKERS=2
# the value is in seconds
ARG GUNICORN_TIMEOUT=60
ARG GUNICORN_LOG_LEVEL=info
ARG DJANGO_BASE_DIR=/usr/src/$PROJECT_NAME
ARG DJANGO_STATIC_ROOT=/var/www/static
ARG DJANGO_MEDIA_ROOT=/var/www/media
ARG DJANGO_SQLITE_DIR=/sqlite
COPY . .

# if no superuser (currently: Admin, Admin, indhhra@bu.edu)
ARG DJANGO_SUPERUSER_USERNAME=admin
ARG DJANGO_SUPERUSER_PASSWORD=admin
ARG DJANGO_SUPERUSER_EMAIL=admin@example.com
ARG DJANGO_DEV_SERVER_PORT=8000
COPY nginx.conf /etc/nginx/nginx.conf

RUN adduser -D -u 1001 user \
&& chown -R user:user /usr/src/mymedic /sqlite \
&& chmod +x /usr/src/mymedic/docker-entrypoint.sh \
/usr/src/mymedic/docker-cmd.sh

ENV \
USER=$USER \
USER_UID=$USER_UID \
PROJECT_NAME=$PROJECT_NAME \
GUNICORN_PORT=$GUNICORN_PORT \
GUNICORN_WORKERS=$GUNICORN_WORKERS \
GUNICORN_TIMEOUT=$GUNICORN_TIMEOUT \
GUNICORN_LOG_LEVEL=$GUNICORN_LOG_LEVEL \
DJANGO_BASE_DIR=$DJANGO_BASE_DIR \
DJANGO_STATIC_ROOT=$DJANGO_STATIC_ROOT \
DJANGO_MEDIA_ROOT=$DJANGO_MEDIA_ROOT \
DJANGO_SQLITE_DIR=$DJANGO_SQLITE_DIR \
DJANGO_SUPERUSER_USERNAME=$DJANGO_SUPERUSER_USERNAME \
DJANGO_SUPERUSER_PASSWORD=$DJANGO_SUPERUSER_PASSWORD \
DJANGO_SUPERUSER_EMAIL=$DJANGO_SUPERUSER_EMAIL \
DJANGO_DEV_SERVER_PORT=$DJANGO_DEV_SERVER_PORT
EXPOSE 80 8000

COPY --from=builder /install /usr/local
COPY docker-entrypoint.sh /
COPY docker-cmd.sh /
COPY $PROJECT_NAME $DJANGO_BASE_DIR

# User
RUN chmod +x /docker-entrypoint.sh /docker-cmd.sh && \
apk --no-cache add su-exec libpq-dev && \
mkdir -p $DJANGO_STATIC_ROOT $DJANGO_MEDIA_ROOT $DJANGO_SQLITE_DIR && \
adduser -s /bin/sh -D -u $USER_UID $USER && \
chown -R $USER:$USER $DJANGO_BASE_DIR $DJANGO_STATIC_ROOT $DJANGO_MEDIA_ROOT $DJANGO_SQLITE_DIR

WORKDIR $DJANGO_BASE_DIR

RUN python manage.py collectstatic --noinput && \
python manage.py makemigrations && \
python manage.py migrate && \
python manage.py createsuperuser --noinput
ENTRYPOINT ["/usr/src/mymedic/docker-entrypoint.sh"]
CMD ["/usr/src/mymedic/docker-cmd.sh"]
28 changes: 13 additions & 15 deletions code/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,31 @@ cp .env.example .env
```

Edit `.env` and add a DJANGO_SECRET_KEY generated from [Django Secret Key Generator](https://djecrety.ir/).
---

### 3. Build the Docker Image

```bash
docker build -t mymedic .
```
Add the email and app passwords for the Gmail account.

---

### 4. Run the Server
### 3. Run the Server

If Docker gives an error about permissions for executing docker-cmd or docker-entrypoint, run the following command to fix it:

#### Running Tests
```bash
docker run --rm mymedic pytest -v tests/
chmod +x docker-cmd.sh
chmod +x docker-entrypoint.sh
```

#### Run in Development Mode
#### Run in Production Mode

```bash
docker compose -f docker-compose-dev.yml up -d
docker compose up --build --force-recreate
```

#### Run in Production Mode
This binds the local website code into the container and serves it at `http://localhost:8080`.

#### To remove old containers and images, run:

```bash
docker compose up -d
docker compose down -v --remove-orphans
```

This binds the local website code into the container and serves it at `http://127.0.0.1:8080`.
---
36 changes: 28 additions & 8 deletions code/docker-cmd.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
#!/bin/sh
set -e

MANAGE_PY=$(find / -type f -name manage.py -print -quit)
if [ -z "$MANAGE_PY" ]; then
echo "Error: manage.py not found under /" >&2
exit 1
fi
cd "$(dirname "$MANAGE_PY")"

su-exec "$USER" python manage.py migrate --noinput

su-exec "$USER" python manage.py collectstatic --noinput

USER_EXISTS="from django.contrib.auth import get_user_model; User = get_user_model(); exit(User.objects.exists())"
su-exec "$USER" python manage.py shell -c "$USER_EXISTS" && su-exec "$USER" python manage.py createsuperuser --noinput
USER_EXISTS_CMD="from django.contrib.auth import get_user_model; U = get_user_model(); exit(0) if U.objects.exists() else exit(1)"
if su-exec "$USER" python manage.py shell -c "$USER_EXISTS_CMD"; then
echo "Superuser already exists"
else
su-exec "$USER" python manage.py createsuperuser --noinput \
--username "$DJANGO_SUPERUSER_USERNAME" \
--email "$DJANGO_SUPERUSER_EMAIL"
fi

nginx

if [ "$1" = "--debug" ]; then
exec su-exec "$USER" python manage.py runserver "0.0.0.0:$DJANGO_DEV_SERVER_PORT"
exec su-exec "$USER" python manage.py runserver "0.0.0.0:${DJANGO_DEV_SERVER_PORT:-8000}"
else
exec su-exec "$USER" gunicorn "$PROJECT_NAME.wsgi:application" \
--bind "0.0.0.0:$GUNICORN_PORT" \
--workers "$GUNICORN_WORKERS" \
--timeout "$GUNICORN_TIMEOUT" \
--log-level "$GUNICORN_LOG_LEVEL"
exec su-exec "$USER" gunicorn "${PROJECT_NAME}.wsgi:application" \
--bind "0.0.0.0:${GUNICORN_PORT:-8000}" \
--workers "${GUNICORN_WORKERS:-3}" \
--timeout "${GUNICORN_TIMEOUT:-30}" \
--log-level "${GUNICORN_LOG_LEVEL:-info}"
fi
19 changes: 0 additions & 19 deletions code/docker-compose-dev.yml

This file was deleted.

41 changes: 38 additions & 3 deletions code/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
services:
mymedic:
container_name: mymedic
build: .
image: mymedic
image: mymedic:latest
env_file:
- .env
restart: unless-stopped

volumes:
- .:/usr/src/mymedic:rw
- sqlite-data:/sqlite
- staticfiles-data:/usr/share/nginx/html/static
- media-data:/usr/share/nginx/html/media

working_dir: /usr/src/mymedic

expose:
- "8000"

entrypoint: ["/usr/src/mymedic/docker-entrypoint.sh"]
command: ["/usr/src/mymedic/docker-cmd.sh"]

nginx:
image: nginx:1.25-alpine
restart: unless-stopped
depends_on:
- mymedic
ports:
- 8080:8000
command: /docker-entrypoint.sh /docker-cmd.sh
- "8080:80"
volumes:
- staticfiles-data:/usr/share/nginx/html/static:ro
- media-data:/usr/share/nginx/html/media:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro

volumes:
sqlite-data:
driver: local
staticfiles-data:
driver: local
media-data:
driver: local
4 changes: 0 additions & 4 deletions code/docker-entrypoint.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
#!/bin/sh
# vim:sw=4:ts=4:et

set -e

su-exec "$USER" python manage.py migrate --noinput

exec "$@"
11 changes: 11 additions & 0 deletions code/mymedic/mymedic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import List, Optional

TRUE = ("1", "true", "True", "TRUE", "on", "yes")


def is_true(val: Optional[str]) -> bool:
return val in TRUE


def split_with_comma(val: str) -> List[str]:
return list(filter(None, map(str.strip, val.split(","))))
Loading