From 38de434ab3cdd11e5def14343ddd648be371a38d Mon Sep 17 00:00:00 2001 From: Anthony Donlon Date: Mon, 29 Dec 2025 01:47:15 +0800 Subject: [PATCH] editor: build docker image for editor server --- .dockerignore | 11 +++++ editor/editor.dockerfile | 78 +++++++++++++++++++++++++++++++ editor/server/app/__init__.py | 4 +- editor/server/config.example.toml | 3 +- editor/server/pyproject.toml | 7 ++- 5 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 editor/editor.dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9700dbf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.vscode/ +.DS_Store +.venv/ +venv/ +node_modules/ +build/ +dist/ +*.egg-info/ +__pycache__/ +.ruff_cache/ +instance/ diff --git a/editor/editor.dockerfile b/editor/editor.dockerfile new file mode 100644 index 0000000..5c53c7c --- /dev/null +++ b/editor/editor.dockerfile @@ -0,0 +1,78 @@ +# To build docker image: +# cd .. +# docker build -t cferr-editor:latest -f editor/editor.dockerfile . + +# ========================= +# Stage 1 — Build frontend +# ========================= +FROM node:24-alpine AS frontend-builder + +WORKDIR /work + +# Install dependencies first (better caching) +COPY ["editor/web/package.json", "editor/web/yarn.lock", "editor/web/"] +COPY ["javascript/package.json", "javascript/package-lock.json", "javascript/"] +RUN cd /work/javascript && npm ci && \ + cd /work/editor/web && yarn install + +# Copy source and build +COPY ["editor/web/", "./editor/web"] +COPY ["resources/", "./resources"] +COPY ["javascript/", "./javascript"] +RUN cd /work/javascript && npm run build && \ + cd /work/editor/web && yarn add ../../javascript && yarn build + +# ========================= +# Stage 2 — Build backend +# ========================= +FROM python:3.14-alpine AS backend-builder + +WORKDIR /work + +# Install dependencies first (better caching) +RUN pip install hatch + +# Copy source and build +COPY ["cloudflare_error_page/", "./cloudflare_error_page"] +COPY ["editor/server/", "./editor/server"] +COPY ["resources/", "./resources"] +COPY ["scripts/", "./scripts"] +COPY ["pyproject.toml", "README.md", "LICENSE.txt", "./"] +COPY --from=frontend-builder /work/editor/web/dist ./web/dist + +RUN hatch build -t wheel && \ + cd editor/server && hatch build -t wheel + +# ========================= +# Stage 3 — Runtime image +# ========================= +FROM python:3.14-alpine + +WORKDIR /app + +# Install some dependencies first (better caching) +RUN pip install gunicorn Flask Flask-Limiter Flask-SqlAlchemy + +# Copy only the built artifacts from the previous stages +COPY --from=frontend-builder /work/editor/web/dist ./web/dist +COPY --from=backend-builder /work/dist/*.whl ./packages/ +COPY --from=backend-builder /work/editor/server/dist/*.whl ./packages/ + +# Install packages +RUN sh -c 'pip install ./packages/*.whl' + +# Optional but recommended: non-root user +RUN adduser -D appuser \ + && chown -R appuser:appuser /app +USER appuser + +# Instance path of the application +VOLUME [ "/data" ] +ENV INSTANCE_PATH=/data +ENV STATIC_DIR=/app/web/dist + +# Expose the port you will serve on +EXPOSE 8000 + +# Start web server +CMD ["gunicorn", "-b", "0.0.0.0:8000", "-w", "1", "app:create_app()"] diff --git a/editor/server/app/__init__.py b/editor/server/app/__init__.py index ff96f62..e11077b 100644 --- a/editor/server/app/__init__.py +++ b/editor/server/app/__init__.py @@ -56,7 +56,9 @@ def _initialize_app_config(app: Flask): 'isolation_level': 'SERIALIZABLE', # "execution_options": {"autobegin": False} } - static_dir = os.path.join(app.instance_path, app.config.get('STATIC_DIR', '../../web/dist')) + static_dir = os.getenv('STATIC_DIR') + if not static_dir: + static_dir = os.path.join(app.instance_path, app.config.get('STATIC_DIR', '../../web/dist')) app.logger.info(f'Static directory: {static_dir}') diff --git a/editor/server/config.example.toml b/editor/server/config.example.toml index 5cf9c48..13af108 100644 --- a/editor/server/config.example.toml +++ b/editor/server/config.example.toml @@ -1,4 +1,5 @@ -# Directory of static files for editor (.html, .js, ...), relative to instance dir +# Directory of static files for editor (.html, .js, ...), relative to instance dir. +# Overridden by environment varable 'STATIC_DIR' STATIC_DIR = '../../web/dist' # Url prefix for app urls diff --git a/editor/server/pyproject.toml b/editor/server/pyproject.toml index e7e0d88..bb6725d 100644 --- a/editor/server/pyproject.toml +++ b/editor/server/pyproject.toml @@ -12,10 +12,15 @@ dependencies = [ "Flask", "Flask-Limiter", "Flask-SqlAlchemy", - "cloudflare-error-page @ {root:uri}/../../", + "cloudflare-error-page", ] version = "0.0.0" +[tool.hatch.envs.default] +dependencies = [ + "cloudflare-error-page @ {root:uri}/../../", +] + [tool.hatch.metadata] allow-direct-references = true