diff --git a/.dockerignore b/.dockerignore index 63ce98d..6391874 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,7 @@ # Django project /media/ /static/ -*.sqlite3 +# *.sqlite3 # Python and others __pycache__ @@ -37,3 +37,6 @@ wheels/ *.egg-info/ .installed.cfg *.egg +.venv/ + +gitea/ diff --git a/Dockerfile b/Dockerfile index 61dad6c..1db602e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,60 +1,57 @@ -# Use an official Python runtime based on Debian 12 "bookworm" as a parent image. -FROM python:3.12-slim-bookworm +FROM python:3.14-slim-bookworm AS builder -# Add user that will be used in the container. -RUN useradd wagtail +RUN useradd -m kursy -# Port used by this container to serve HTTP. -EXPOSE 8000 +ENV NODE_VERSION=24.14.1 -# Set environment variables. -# 1. Force Python stdout and stderr streams to be unbuffered. -# 2. Set PORT variable that is used by Gunicorn. This should match "EXPOSE" -# command. -ENV PYTHONUNBUFFERED=1 \ - PORT=8000 +# install uv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ -# Install system packages required by Wagtail and Django. -RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \ - build-essential \ - libpq-dev \ - libmariadb-dev \ - libjpeg62-turbo-dev \ - zlib1g-dev \ - libwebp-dev \ - && rm -rf /var/lib/apt/lists/* +# install nodejs +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get install -y nodejs -# Install the application server. -RUN pip install "gunicorn==20.0.4" +# copy project files +WORKDIR /app +COPY pyproject.toml uv.lock ./ -# Install the project requirements. -COPY requirements.txt / -RUN pip install -r /requirements.txt +RUN chown kursy:kursy /app -R -# Use /app folder as a directory where the source code is stored. +USER kursy + +# install project dependencies +RUN uv sync + +COPY --chown=kursy:kursy . . + +# install tailwind +WORKDIR /app/theme/static_src +RUN npm install + +# collect static files +WORKDIR /app +RUN uv run python manage.py collectstatic --noinput --clear + + +# --- RUNTIME IMAGE --- +FROM python:3.14-slim-bookworm + +RUN useradd -m kursy WORKDIR /app -# Set this directory to be owned by the "wagtail" user. This Wagtail project -# uses SQLite, the folder needs to be owned by the user that -# will be writing to the database file. -RUN chown wagtail:wagtail /app +RUN mkdir -p /app/data && chown kursy:kursy /app/data -# Copy the source code of the project into the container. -COPY --chown=wagtail:wagtail . . +USER kursy -# Use user "wagtail" to run the build commands below and the server itself. -USER wagtail +COPY --from=builder --chown=kursy:kursy /app/.venv /app/.venv +COPY --from=builder --chown=kursy:kursy /app /app -# Collect static files. -RUN python manage.py collectstatic --noinput --clear +ENV PATH="/app/.venv/bin:$PATH" \ + PYTHONUNBUFFERED=1 \ + PORT=8000 + +EXPOSE 8000 + +CMD set -xe; python manage.py migrate --noinput; python manage.py runserver 0.0.0.0:8000 -# Runtime command that executes when "docker run" is called, it does the -# following: -# 1. Migrate the database. -# 2. Start the application server. -# WARNING: -# Migrating database at the same time as starting the server IS NOT THE BEST -# PRACTICE. The database should be migrated manually or using the release -# phase facilities of your hosting platform. This is used only so the -# Wagtail instance can be started with a simple "docker run" command. -CMD set -xe; python manage.py migrate --noinput; gunicorn kursy.wsgi:application diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ed06db2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.8" + +networks: + gitea: + external: false + +services: + gitea: + image: docker.gitea.com/gitea:latest + container_name: gitea + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=sqlite3 + - GITEA__server__ROOT_URL=http://localhost:3000/ + restart: always + networks: + - gitea + volumes: + - ./gitea/data:/data + # - ./gitea/config:/data/gitea/conf + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "3022:3022" + + # kursy: + # build: . + # container_name: kursy + # restart: unless-stopped + # networks: + # - gitea + # volumes: + # - ./db:/app/db + # ports: + # - "8000:8000" diff --git a/kursy/settings/base.py b/kursy/settings/base.py index b5393f5..85251e1 100644 --- a/kursy/settings/base.py +++ b/kursy/settings/base.py @@ -161,7 +161,8 @@ OAUTH2_PROVIDER = { DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", + # "NAME": BASE_DIR / "db" / "db.sqlite3", + "NAME": "/app/db/db.sqlite3", } } diff --git a/pyproject.toml b/pyproject.toml index 4b62248..777f08d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,8 +11,11 @@ dependencies = [ "django-oauth-toolkit>=3.2.0", "django-tailwind>=4.4.2", "django-widget-tweaks>=1.5.1", + "gunicorn>=25.3.0", "python-dotenv>=1.2.2", "slippers>=0.6.2", + "uvicorn>=0.42.0", + "uvicorn-worker>=0.4.0", "wagtail==7.3rc1", "wagtail-color-panel>=1.7.1", "wagtailmedia>=0.17.2", diff --git a/uv.lock b/uv.lock index 67eb1b1..5f11041 100644 --- a/uv.lock +++ b/uv.lock @@ -450,6 +450,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, ] +[[package]] +name = "gunicorn" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/f4/e78fa054248fab913e2eab0332c6c2cb07421fca1ce56d8fe43b6aef57a4/gunicorn-25.3.0.tar.gz", hash = "sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889", size = 634883, upload-time = "2026-03-27T00:00:26.092Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/c8/8aaf447698c4d59aa853fd318eed300b5c9e44459f242ab8ead6c9c09792/gunicorn-25.3.0-py3-none-any.whl", hash = "sha256:cacea387dab08cd6776501621c295a904fe8e3b7aae9a1a3cbb26f4e7ed54660", size = 208403, upload-time = "2026-03-27T00:00:27.386Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + [[package]] name = "idna" version = "3.11" @@ -497,8 +518,11 @@ dependencies = [ { name = "django-oauth-toolkit" }, { name = "django-tailwind" }, { name = "django-widget-tweaks" }, + { name = "gunicorn" }, { name = "python-dotenv" }, { name = "slippers" }, + { name = "uvicorn" }, + { name = "uvicorn-worker" }, { name = "wagtail" }, { name = "wagtail-color-panel" }, { name = "wagtailmedia" }, @@ -514,8 +538,11 @@ requires-dist = [ { name = "django-oauth-toolkit", specifier = ">=3.2.0" }, { name = "django-tailwind", specifier = ">=4.4.2" }, { name = "django-widget-tweaks", specifier = ">=1.5.1" }, + { name = "gunicorn", specifier = ">=25.3.0" }, { name = "python-dotenv", specifier = ">=1.2.2" }, { name = "slippers", specifier = ">=0.6.2" }, + { name = "uvicorn", specifier = ">=0.42.0" }, + { name = "uvicorn-worker", specifier = ">=0.4.0" }, { name = "wagtail", specifier = "==7.3rc1" }, { name = "wagtail-color-panel", specifier = ">=1.7.1" }, { name = "wagtailmedia", specifier = ">=0.17.2" }, @@ -618,6 +645,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, ] +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + [[package]] name = "pillow" version = "12.1.1" @@ -900,6 +936,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] +[[package]] +name = "uvicorn" +version = "0.42.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e3/ad/4a96c425be6fb67e0621e62d86c402b4a17ab2be7f7c055d9bd2f638b9e2/uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775", size = 85393, upload-time = "2026-03-16T06:19:50.077Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/89/f8827ccff89c1586027a105e5630ff6139a64da2515e24dafe860bd9ae4d/uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", size = 68830, upload-time = "2026-03-16T06:19:48.325Z" }, +] + +[[package]] +name = "uvicorn-worker" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gunicorn" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/59/9101b9c0680fd80e9d26c07deb822a5d18a324339fcf9cd017885ee808ad/uvicorn_worker-0.4.0.tar.gz", hash = "sha256:8ee5306070d8f38dce124adce488c3c0b50f20cf0c0222b12c66188da7214493", size = 9361, upload-time = "2025-09-20T10:47:01.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/25/09cd7a90c8bb7fb693be0d6704fccd5f9778d5513214b7a01cc4a94ff314/uvicorn_worker-0.4.0-py3-none-any.whl", hash = "sha256:e2ed952cef976f5e9e429d7269640bbcafbd36c80aa80f1003c8c77a6797abde", size = 5364, upload-time = "2025-09-20T10:46:59.776Z" }, +] + [[package]] name = "wagtail" version = "7.3rc1"