From be451bda1e2292c2e1b121a99fe158e254f9e7e5 Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Wed, 17 Dec 2025 18:07:12 +0100 Subject: [PATCH] dev: switch to fully containerized Docker Compose workflow - Replace tmux setup with a full Docker Compose development stack - Update backend Dockerfile to use `cargo-chef` caching and `supervisord` - Containerize frontend development server - Update tests to run in isolated containers - Remove legacy `papercrate.tmux` --- DEVELOPMENT.md | 83 +++++++----- backend/.dockerignore | 1 + backend/Dockerfile | 259 +++++++++++++++++++++++++------------ backend/diesel.toml | 4 - backend/run-dev.sh | 16 +++ backend/supervisord.conf | 67 ++++++++++ docker-compose.dev.yml | 67 +++++++--- docker-compose.test.yml | 21 +-- frontend/Dockerfile | 24 +++- frontend/webpack.config.js | 2 +- papercrate.tmux | 11 -- 11 files changed, 396 insertions(+), 159 deletions(-) create mode 100644 backend/.dockerignore create mode 100755 backend/run-dev.sh create mode 100644 backend/supervisord.conf delete mode 100644 papercrate.tmux diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 874afbe..7f48915 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -5,15 +5,40 @@ integration testing, and infrastructure automation. ## Local Development -Use the provided `papercrate.tmux` to spin up the full stack in one tmux session: +The entire application stack (frontend, backend, worker, database, minio, quickwit) runs fully containerized via Docker Compose. + +### Start Development Environment + +To start the stack (builds are handled automatically): ```bash -tmux -f papercrate.tmux attach +docker compose -f docker-compose.dev.yml up --build ``` -This creates windows for the compose stack, frontend dev server, backend API, and -background worker using the repository-relative paths defined in the tmux file. -Detach with `Ctrl+b d` and reattach later with the same command. +### Apply Code Changes + +Hot-reloading is handled automatically by `cargo watch` inside the container. +When you save files in `backend/src`, the watcher will: + +1. Rebuild the modified binaries. +2. Restart the `backend`, `worker`, and `webdav` services via `supervisord`. + +No manual restart is required. + +### Running Migrations + +Since `diesel-cli` runs inside the container: + +```bash +# Run pending migrations +docker compose -f docker-compose.dev.yml exec server diesel migration run + +# Revert last migration +docker compose -f docker-compose.dev.yml exec server diesel migration revert + +# Create new migration +docker compose -f docker-compose.dev.yml exec server diesel migration generate name_of_migration +``` The development Postgres container now seeds two database roles: @@ -27,21 +52,21 @@ role with `SET ROLE papercrate_app_login;` before querying tenant tables. ## Backend Integration Tests -Integration tests require a running Postgres instance (and, optionally, Quickwit -for OCR indexing). The repository includes a lightweight compose file for local -runs: +Integration tests run in a dedicated, ephemeral container stack. The repository includes a lightweight compose file that provisions a fresh Postgres instance (using tmpfs) and Quickwit for every run. + +To run the tests: ```bash -docker compose -f docker-compose.test.yml up -d -export TEST_DATABASE_URL=postgres://papercrate:papercrate_test@localhost:5433/papercrate_test -# optional, enables Quickwit indexing jobs -export QUICKWIT_ENDPOINT=http://localhost:7280 -export QUICKWIT_INDEX=documents -cargo test +docker compose -f docker-compose.test.yml run --rm test-runner ``` -Stop the database when you are done: +This will: +1. Spin up `postgres-test` and `quickwit-test` (in background if not running). +2. Start the `test-runner` container. +3. Wait for DB, run migrations, and execute `cargo test`. +4. Remove the runner container after exit. +To clean up the infrastructure afterwards: ```bash docker compose -f docker-compose.test.yml down ``` @@ -50,14 +75,8 @@ The compose service uses tmpfs storage, giving each test run a clean database. ## Runtime Dependencies -- `ocrmypdf` (optional but recommended): Used by the OCR worker to extract text - from PDFs when no embedded text layer is available. Ensure it is installed and - available on the worker hosts if OCR is desired. -- Quickwit (optional): The Quickwit indexer is used to ingest extracted text for - search. Set `QUICKWIT_ENDPOINT` and `QUICKWIT_INDEX` in the environment when - running workers if you want indexing jobs to run. The local compose file starts - a Quickwit instance on `http://localhost:7280` and seeds the `documents` index - automatically. +- `ocrmypdf`: Used by the worker to extract text from images. If missing, the worker logs a warning and skips text extraction for that document. +- `Quickwit`: Used for full-text search. If configured (via `QUICKWIT_ENDPOINT`), the worker pushes extracted text to the index. If missing, search features will simply be unavailable. ## Configuration @@ -76,9 +95,8 @@ runtime settings in staging without exposing credentials. ## Running Migrations in Kubernetes -The backend container image ships the `diesel` CLI, so schema migrations can be -executed as a short-lived Job (or Helm hook) before rolling out new pods. Example -manifest: +The backend container image ships with the `papercrate-admin` binary, which can execute schema migrations +as a short-lived Job (or Helm hook) before rolling out new pods. Example manifest: ```yaml apiVersion: batch/v1 @@ -92,16 +110,19 @@ spec: containers: - name: migrate image: ghcr.io/example/papercrate-backend: - command: ["/usr/local/bin/diesel", "migration", "run"] + command: ["/usr/local/bin/papercrate-admin", "migrate-database"] env: - name: DATABASE_URL valueFrom: secretKeyRef: name: papercrate-db key: DATABASE_URL + + - name: MIGRATIONS_DATABASE_URL + valueFrom: + secretKeyRef: + name: papercrate-db + key: DATABASE_URL ``` -Run the Job manually (`kubectl apply -f migrate-job.yaml`) or configure it as a -Helm pre-install/pre-upgrade hook so migrations run automatically on each -deployment. Once the Job succeeds, deploy/update the backend `Deployment` as -usual. +Run the Job manually or use the Helm hooks configured in `k8s/papercrate/templates/migrate-job.yaml`. The `papercrate-admin` binary is built specifically for administrative tasks. diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..9f97022 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index ffce2ca..4f3945f 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,82 +1,192 @@ -# syntax=docker/dockerfile:1.7 - +# ------------------------------------------------------------------------------ +# Global Arguments +# ------------------------------------------------------------------------------ ARG RUST_VERSION=1 -FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim AS builder +ARG RUNTIME_DEPS="ocrmypdf tesseract-ocr ghostscript qpdf ffmpeg" + +# ------------------------------------------------------------------------------ +# Base Stage: Shared Logic (PDFium) +# ------------------------------------------------------------------------------ +FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim AS base +ARG RUNTIME_DEPS +WORKDIR /app + +# Install PDFium +ARG TARGETARCH +RUN set -eux; \ + case "${TARGETARCH}" in \ + amd64|x86_64) pdfium_package=pdfium-linux-x64.tgz ;; \ + arm64|aarch64) pdfium_package=pdfium-linux-arm64.tgz ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ + esac; \ + apt-get update && apt-get install -y --no-install-recommends curl ca-certificates; \ + curl -fsSL "https://github.com/bblanchon/pdfium-binaries/releases/latest/download/${pdfium_package}" -o /tmp/pdfium.tgz; \ + mkdir -p /tmp/pdfium; \ + tar -xzf /tmp/pdfium.tgz -C /tmp/pdfium --strip-components=1; \ + pdfium_so="$(find /tmp/pdfium -name libpdfium.so -type f | head -n1)"; \ + [ -n "${pdfium_so}" ]; \ + mkdir -p /usr/local/lib; \ + cp "${pdfium_so}" /usr/local/lib/libpdfium.so; \ + rm -rf /tmp/pdfium.tgz /tmp/pdfium + +# Install common build dependencies AND runtime deps (for dev/testing) +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + pkg-config \ + libssl-dev \ + libpq-dev \ + libjpeg-dev \ + libpng-dev \ + zlib1g-dev \ + ca-certificates \ + curl \ + git \ + ${RUNTIME_DEPS} \ + && rm -rf /var/lib/apt/lists/* + +# ------------------------------------------------------------------------------ +# Development Stage +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# Chef Stage: Install cargo-chef (used for caching) +# ------------------------------------------------------------------------------ +FROM base AS chef +RUN cargo install cargo-chef + +# ------------------------------------------------------------------------------ +# Planner Stage: Compute lockfile recipe +# ------------------------------------------------------------------------------ +FROM chef AS planner +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +# ------------------------------------------------------------------------------ +# Cacher Stage: Build dependencies only +# ------------------------------------------------------------------------------ +FROM chef AS cacher +ENV CARGO_TARGET_DIR=/cargo-target +COPY --from=planner /app/recipe.json recipe.json +# Build dependencies (including test deps) based on the recipe +RUN cargo chef cook --tests --recipe-path recipe.json + +# ------------------------------------------------------------------------------ +# Development Stage +# ------------------------------------------------------------------------------ +FROM base AS development +WORKDIR /app + +# Install additional development tools +RUN apt-get update && apt-get install -y --no-install-recommends \ + procps \ + postgresql-client \ + supervisor \ + && rm -rf /var/lib/apt/lists/* + +# Install diesel-cli for migrations +RUN cargo install diesel_cli --no-default-features --features postgres + +# Install cargo-watch for hot reloading +RUN cargo install cargo-watch + +# Setup Cache +ENV CARGO_TARGET_DIR=/cargo-target +COPY --from=cacher /cargo-target /cargo-target +COPY --from=cacher /usr/local/cargo /usr/local/cargo + +# Copy PDFium from base +COPY --from=base /usr/local/lib/libpdfium.so /usr/local/lib/libpdfium.so +ENV LD_LIBRARY_PATH=/usr/local/lib +RUN ldconfig + +ENV RUST_LOG=info +CMD ["./run-dev.sh"] + +# ------------------------------------------------------------------------------ +# Production Builder Stage +# ------------------------------------------------------------------------------ +# We restart from base to keep the image clean, but copy PDFium if needed for build/tests +FROM base AS builder ARG TARGETARCH ENV TARGETARCH=${TARGETARCH} WORKDIR /app +# Resolve cross-compilation target script RUN cat <<'SCRIPT' >/usr/local/bin/resolve-target.sh #!/bin/sh set -e case "$1" in - amd64) - echo x86_64-unknown-linux-gnu - ;; - arm64) - echo aarch64-unknown-linux-gnu - ;; - *) - echo "Unsupported TARGETARCH: $1" >&2 - exit 1 - ;; + amd64) echo x86_64-unknown-linux-gnu ;; + arm64) echo aarch64-unknown-linux-gnu ;; + *) echo "Unsupported TARGETARCH: $1" >&2; exit 1 ;; esac SCRIPT RUN chmod +x /usr/local/bin/resolve-target.sh -ENV PKG_CONFIG_ALLOW_CROSS=1 \ - CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \ - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ - PKG_CONFIG_LIBDIR_aarch64_unknown_linux_gnu=/usr/lib/aarch64-linux-gnu/pkgconfig \ - PKG_CONFIG_PATH_aarch64_unknown_linux_gnu=/usr/lib/aarch64-linux-gnu/pkgconfig \ - PKG_CONFIG_SYSROOT_DIR_aarch64_unknown_linux_gnu=/usr/aarch64-linux-gnu \ - OPENSSL_DIR_aarch64_unknown_linux_gnu=/usr/lib/aarch64-linux-gnu \ - OPENSSL_LIB_DIR_aarch64_unknown_linux_gnu=/usr/lib/aarch64-linux-gnu \ - OPENSSL_INCLUDE_DIR_aarch64_unknown_linux_gnu=/usr/include/aarch64-linux-gnu \ - CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc \ - CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc \ - PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu=/usr/lib/x86_64-linux-gnu/pkgconfig \ - PKG_CONFIG_PATH_x86_64_unknown_linux_gnu=/usr/lib/x86_64-linux-gnu/pkgconfig \ - OPENSSL_DIR_x86_64_unknown_linux_gnu=/usr/lib/x86_64-linux-gnu \ - OPENSSL_LIB_DIR_x86_64_unknown_linux_gnu=/usr/lib/x86_64-linux-gnu \ - OPENSSL_INCLUDE_DIR_x86_64_unknown_linux_gnu=/usr/include/x86_64-linux-gnu +# Install cross-compilation deps +ARG BUILDPLATFORM +RUN if [ "${TARGETARCH}" = "amd64" ]; then \ + echo "x86_64-linux-gnu" > /tmp/target_deb_arch; \ + elif [ "${TARGETARCH}" = "arm64" ]; then \ + echo "aarch64-linux-gnu" > /tmp/target_deb_arch; \ + else \ + echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1; \ + fi RUN set -eux; \ - case "${TARGETARCH}" in \ - amd64) TARGET_DEB_ARCH=amd64 ;; \ - arm64) TARGET_DEB_ARCH=arm64 ;; \ - *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ - esac; \ - BUILD_DEB_ARCH="$(dpkg --print-architecture)"; \ - if [ "${TARGET_DEB_ARCH}" != "${BUILD_DEB_ARCH}" ]; then \ - dpkg --add-architecture "${TARGET_DEB_ARCH}"; \ - fi; \ - apt-get update; \ - apt-get install -y --no-install-recommends \ - build-essential \ - pkg-config \ - curl \ - libssl-dev \ - libpq-dev \ - libjpeg-dev \ - libpng-dev \ - zlib1g-dev; \ - if [ "${TARGET_DEB_ARCH}" != "${BUILD_DEB_ARCH}" ]; then \ + # Detect build arch (assuming debian-like names compatible with apt) + dpkg_arch="$(dpkg --print-architecture)"; \ + target_deb_arch="$(cat /tmp/target_deb_arch)"; \ + \ + # If we are cross-compiling + if [ "${dpkg_arch}" != "${TARGETARCH}" ]; then \ + # Map target arch to debian package arch suffix if needed, but usually apt handles :arch + # For cross-compiling, we need to add the architecture + dpkg --add-architecture "${TARGETARCH}"; \ + apt-get update; \ + \ case "${TARGETARCH}" in \ arm64) CROSS_GCC=gcc-aarch64-linux-gnu ;; \ amd64) CROSS_GCC=gcc-x86-64-linux-gnu ;; \ esac; \ + \ apt-get install -y --no-install-recommends \ "${CROSS_GCC}" \ - "libc6-dev:${TARGET_DEB_ARCH}" \ - "libssl-dev:${TARGET_DEB_ARCH}" \ - "libpq-dev:${TARGET_DEB_ARCH}" \ - "libjpeg-dev:${TARGET_DEB_ARCH}" \ - "libpng-dev:${TARGET_DEB_ARCH}" \ - "zlib1g-dev:${TARGET_DEB_ARCH}"; \ + "libc6-dev:${TARGETARCH}" \ + "libssl-dev:${TARGETARCH}" \ + "libpq-dev:${TARGETARCH}" \ + "libjpeg-dev:${TARGETARCH}" \ + "libpng-dev:${TARGETARCH}" \ + "zlib1g-dev:${TARGETARCH}"; \ + \ + # Configure PKG_CONFIG and LINKER to find foreign libraries + case "${TARGETARCH}" in \ + "amd64") \ + GNU_ARCH="x86_64-linux-gnu" \ + RUST_ARCH="x86_64_unknown_linux_gnu" \ + RUST_ARCH_UPPER="X86_64_UNKNOWN_LINUX_GNU" \ + ;; \ + "arm64") \ + GNU_ARCH="aarch64-linux-gnu" \ + RUST_ARCH="aarch64_unknown_linux_gnu" \ + RUST_ARCH_UPPER="AARCH64_UNKNOWN_LINUX_GNU" \ + ;; \ + esac; \ + \ + { \ + echo "export PKG_CONFIG_ALLOW_CROSS=1"; \ + echo "export PKG_CONFIG_PATH=/usr/lib/${GNU_ARCH}/pkgconfig"; \ + echo "export OPENSSL_DIR=/usr/lib/${GNU_ARCH}"; \ + echo "export OPENSSL_LIB_DIR=/usr/lib/${GNU_ARCH}"; \ + echo "export OPENSSL_INCLUDE_DIR=/usr/include/${GNU_ARCH}"; \ + echo "export CARGO_TARGET_${RUST_ARCH_UPPER}_LINKER=${GNU_ARCH}-gcc"; \ + echo "export CC_${RUST_ARCH}=${GNU_ARCH}-gcc"; \ + echo "export CXX_${RUST_ARCH}=${GNU_ARCH}-g++"; \ + } >> /etc/profile; \ fi; \ rm -rf /var/lib/apt/lists/* +ENV PKG_CONFIG_ALLOW_CROSS=1 + COPY Cargo.toml Cargo.lock build.rs ./ COPY src ./src COPY migrations ./migrations @@ -85,6 +195,9 @@ COPY resources ./resources COPY diesel.toml ./ RUN set -eux; \ + echo "Loading cross-compilation environment..."; \ + . /etc/profile; \ + export PATH="$PATH:/usr/local/cargo/bin"; \ TARGET="$(/usr/local/bin/resolve-target.sh "${TARGETARCH}")"; \ rustup target add "${TARGET}"; \ cargo build --release --target "${TARGET}" --bin backend --bin worker --bin webdav --bin admin; \ @@ -93,37 +206,18 @@ RUN set -eux; \ cp "target/${TARGET}/release/${bin}" "/artifacts/${bin}"; \ done -RUN set -eux; \ - case "${TARGETARCH}" in \ - amd64) pdfium_package=pdfium-linux-x64.tgz ;; \ - arm64) pdfium_package=pdfium-linux-arm64.tgz ;; \ - *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ - esac; \ - curl -fsSL "https://github.com/bblanchon/pdfium-binaries/releases/latest/download/${pdfium_package}" -o /tmp/pdfium.tgz; \ - mkdir -p /tmp/pdfium; \ - tar -xzf /tmp/pdfium.tgz -C /tmp/pdfium --strip-components=1; \ - pdfium_so="$(find /tmp/pdfium -name libpdfium.so -type f | head -n1)"; \ - [ -n "${pdfium_so}" ]; \ - cp "${pdfium_so}" /artifacts/libpdfium.so; \ - rm -rf /tmp/pdfium.tgz /tmp/pdfium - -FROM --platform=$TARGETPLATFORM debian:trixie-slim AS runtime +# ------------------------------------------------------------------------------ +# Production Runtime Stage +# ------------------------------------------------------------------------------ +FROM debian:trixie-slim AS runtime ARG TARGETARCH +ARG RUNTIME_DEPS WORKDIR /app RUN apt-get update \ && apt-get install -y --no-install-recommends \ - ca-certificates \ - curl \ - libssl3 \ - libpq5 \ - libjpeg62-turbo \ - libpng16-16 \ - ocrmypdf \ - tesseract-ocr \ - ghostscript \ - qpdf \ - ffmpeg \ + ca-certificates curl libssl3 libpq5 libjpeg62-turbo libpng16-16 \ + ${RUNTIME_DEPS} \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /usr/local/lib \ && useradd --system --create-home --uid 10001 appuser @@ -132,7 +226,8 @@ COPY --from=builder /artifacts/backend /usr/local/bin/papercrate-backend COPY --from=builder /artifacts/worker /usr/local/bin/papercrate-worker COPY --from=builder /artifacts/webdav /usr/local/bin/papercrate-webdav COPY --from=builder /artifacts/admin /usr/local/bin/papercrate-admin -COPY --from=builder /artifacts/libpdfium.so /usr/local/lib/libpdfium.so +COPY --from=base /usr/local/lib/libpdfium.so /usr/local/lib/libpdfium.so + RUN ldconfig COPY migrations ./migrations diff --git a/backend/diesel.toml b/backend/diesel.toml index c3b66ee..4201b85 100644 --- a/backend/diesel.toml +++ b/backend/diesel.toml @@ -1,6 +1,2 @@ -[print_schema] -file = "src/schema.rs" -custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] - [migrations_directory] dir = "migrations" \ No newline at end of file diff --git a/backend/run-dev.sh b/backend/run-dev.sh new file mode 100755 index 0000000..d6c76e8 --- /dev/null +++ b/backend/run-dev.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e + +echo "Waiting for Postgres..." +until pg_isready -h postgres -U papercrate; do + sleep 1 +done + +echo "Running migrations..." +diesel migration run + +echo "Building binaries for first run..." +cargo build --bin backend --bin worker --bin webdav + +echo "Starting supervisord..." +exec supervisord -c supervisord.conf diff --git a/backend/supervisord.conf b/backend/supervisord.conf new file mode 100644 index 0000000..457dced --- /dev/null +++ b/backend/supervisord.conf @@ -0,0 +1,67 @@ +[supervisord] +nodaemon=true +user=root +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/var/run/supervisord.pid + +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0700 + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock + +# ------------------------------------------------------------------------------ +# Applications +# ------------------------------------------------------------------------------ + +[program:backend] +command=/cargo-target/debug/backend +directory=/app +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:worker] +command=/cargo-target/debug/worker +directory=/app +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:webdav] +command=/cargo-target/debug/webdav +directory=/app +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +# ------------------------------------------------------------------------------ +# Watcher +# ------------------------------------------------------------------------------ + +[program:watcher] +# 1. Check syntax +# 2. Build binaries +# 3. Restart services via supervisorctl +command=cargo watch -w src -x check -x "build --bin backend --bin worker --bin webdav" -s "supervisorctl restart backend worker webdav" +directory=/app +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index ece5879..3410720 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,3 +1,17 @@ +x-backend-env: &backend-env + DATABASE_URL: postgres://papercrate:papercrate_dev@postgres:5432/papercrate + DATABASE_MAX_POOL_SIZE: 2 + AWS_ENDPOINT_URL: http://minio:9000 + AWS_ACCESS_KEY_ID: minioadmin + AWS_SECRET_ACCESS_KEY: minioadmin + AWS_REGION: us-east-1 + S3_BUCKET: documents + PROXY_DOWNLOADS: "true" + JWT_SECRET: change-me-super-secret + QUICKWIT_ENDPOINT: http://quickwit:7280 + WEBAUTHN_RP_ID: localhost + WEBAUTHN_ORIGIN: http://localhost:5173 + services: postgres: image: postgres:16-alpine @@ -62,32 +76,49 @@ services: timeout: 5s retries: 5 - admin-bootstrap: + server: build: context: ./backend + dockerfile: Dockerfile + target: development + network: host + volumes: + - ./backend:/app + environment: + <<: *backend-env + RUST_LOG: info + SERVER_HOST: 0.0.0.0 + SERVER_PORT: 3000 + ports: + - "3000:3000" + - "3001:3001" depends_on: postgres: condition: service_healthy + minio: + condition: service_healthy quickwit: condition: service_healthy + createbuckets: + condition: service_completed_successfully + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + target: development + network: host + volumes: + - ./frontend:/app + - /app/node_modules + - /app/dist + ports: + - "5173:5173" environment: - DATABASE_URL: postgres://papercrate:papercrate_dev@postgres:5432/papercrate - DATABASE_MAX_POOL_SIZE: 2 - AWS_ENDPOINT_URL: http://minio:9000 - AWS_ACCESS_KEY_ID: minioadmin - AWS_SECRET_ACCESS_KEY: minioadmin - AWS_REGION: us-east-1 - S3_BUCKET: documents - JWT_SECRET: change-me-super-secret - QUICKWIT_ENDPOINT: http://quickwit:7280 - entrypoint: [] - command: > - /bin/sh -c " - echo 'Running database migrations' && - diesel migration run - " - user: root - restart: "no" + API_PROXY_PASS: http://server:3000 + depends_on: + server: + condition: service_started volumes: postgres_data: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index dc8425a..c478726 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -31,28 +31,33 @@ services: timeout: 5s retries: 5 - admin-bootstrap: + test-runner: build: context: ./backend + dockerfile: Dockerfile + target: development depends_on: postgres-test: condition: service_healthy quickwit-test: condition: service_healthy + volumes: + - ./backend:/app + - /app/target environment: - DATABASE_URL: postgres://papercrate:papercrate_test@postgres-test:5432/papercrate_test + TEST_DATABASE_URL: postgres://papercrate:papercrate_test@postgres-test:5432/papercrate_test DATABASE_MAX_POOL_SIZE: 1 S3_BUCKET: documents JWT_SECRET: change-me-super-secret QUICKWIT_ENDPOINT: http://quickwit-test:7280 - entrypoint: [] + RUST_LOG: info command: > - /bin/sh -c " - echo 'Running database migrations' && - diesel migration run + /bin/bash -c " + echo 'Waiting for Postgres...' && + until pg_isready -h postgres-test -U papercrate; do sleep 1; done && + echo 'Running tests...' && + cargo test " - user: root - restart: "no" volumes: quickwit_test_data: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 21efd0b..00c49b5 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -3,16 +3,32 @@ ARG NODE_IMAGE=node:20-alpine ARG NGINX_IMAGE=nginx:alpine -FROM --platform=$BUILDPLATFORM ${NODE_IMAGE} AS build +# ------------------------------------------------------------------------------ +# Base Stage: Install Dependencies +# ------------------------------------------------------------------------------ +FROM --platform=$BUILDPLATFORM ${NODE_IMAGE} AS base WORKDIR /app - COPY package.json package-lock.json ./ -RUN npm ci --no-audit --no-fund +RUN npm install +# ------------------------------------------------------------------------------ +# Development Stage +# ------------------------------------------------------------------------------ +FROM base AS development +EXPOSE 5173 +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] + +# ------------------------------------------------------------------------------ +# Build Stage +# ------------------------------------------------------------------------------ +FROM base AS build COPY . . RUN npm run build -FROM ${NGINX_IMAGE} +# ------------------------------------------------------------------------------ +# Runtime Stage +# ------------------------------------------------------------------------------ +FROM ${NGINX_IMAGE} AS runtime WORKDIR /usr/share/nginx/html COPY --from=build /app/dist ./ diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 364d91a..10f2a2e 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -65,7 +65,7 @@ module.exports = { proxy: [ { context: ['/api', '/download'], - target: 'http://127.0.0.1:3000', + target: process.env.API_PROXY_PASS, changeOrigin: true, secure: false, }, diff --git a/papercrate.tmux b/papercrate.tmux deleted file mode 100644 index ff28191..0000000 --- a/papercrate.tmux +++ /dev/null @@ -1,11 +0,0 @@ -# tmux session for Papercrate dev stack -new-session -d -s papercrate -n docker -c . 'docker compose -f docker-composer.dev.yml up' - -new-window -t papercrate:1 -n frontend -c ./frontend 'npm run dev' - -new-window -t papercrate:2 -n backend -c ./backend 'cargo run --bin backend' - -new-window -t papercrate:3 -n worker -c ./backend 'cargo run --bin worker' - -select-window -t papercrate:0 -attach-session -t papercrate