- storage.py: atomic save_json (temp+fsync+os.replace) and load_json that survives missing/corrupt files (moves corrupt aside). Closes #11/#12. - route all data modules + app.py helpers through storage (closes #31 core); fixes mutable DEFAULT_FUNDS/DEFAULT_CARS return (#23). - gunicorn --workers 1 --threads 4 to remove the write race (#13); strip Dockerfile template scaffolding (#40). - base.html: 💡 suggestions link in the shared nav (shows on every page). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
30 lines
959 B
Docker
30 lines
959 B
Docker
# Default image for a pip-based Python web app (Flask via gunicorn).
|
|
# ADJUST the CMD at the bottom to match the app's entry point.
|
|
FROM python:3.12-slim
|
|
|
|
WORKDIR /app
|
|
ENV PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
|
|
|
|
# sqlite3 CLI is used by the backup snapshot; curl for healthchecks.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends sqlite3 curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY requirements.txt .
|
|
RUN pip install -r requirements.txt && pip install gunicorn requests
|
|
|
|
COPY . .
|
|
|
|
# Persistent data lives on a mounted volume, not in the image.
|
|
RUN mkdir -p /data/uploads/suggestions
|
|
|
|
COPY entrypoint.sh /entrypoint.sh
|
|
RUN chmod +x /entrypoint.sh
|
|
|
|
EXPOSE 8000
|
|
ENTRYPOINT ["/entrypoint.sh"]
|
|
|
|
# Single worker + threads: the app stores state in JSON/SQLite files on the
|
|
# volume, so multiple processes would race. Threads handle light concurrency.
|
|
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "1", "--threads", "4", "app:app"]
|