prodv1
This commit is contained in:
22
.dockerignore
Normal file
22
.dockerignore
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
instance/
|
||||||
|
app/static/uploads/
|
||||||
|
|
||||||
|
.env
|
||||||
|
.flaskenv
|
||||||
|
|
||||||
|
*.sqlite
|
||||||
|
|
||||||
|
scripts/
|
||||||
|
|
||||||
|
README.md
|
||||||
30
Dockerfile
Normal file
30
Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# System deps (kept minimal). Pillow may need some libs; for most cases this is fine on slim.
|
||||||
|
# If you hit Pillow build/runtime issues, consider adding: libjpeg62-turbo, zlib1g, etc.
|
||||||
|
|
||||||
|
COPY requirements.txt ./
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt \
|
||||||
|
&& pip install --no-cache-dir gunicorn
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Create runtime dirs (also mountable as volumes)
|
||||||
|
RUN mkdir -p instance app/static/uploads
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Default config (override at runtime)
|
||||||
|
ENV FLASK_ENV=production \
|
||||||
|
GUNICORN_WORKERS=2 \
|
||||||
|
GUNICORN_BIND=0.0.0.0:8000
|
||||||
|
|
||||||
|
# Run via WSGI entrypoint
|
||||||
|
CMD ["sh", "-c", "gunicorn -w ${GUNICORN_WORKERS} -b ${GUNICORN_BIND} wsgi:app"]
|
||||||
98
README.md
98
README.md
@@ -33,6 +33,100 @@ flask run --debug
|
|||||||
|
|
||||||
Open http://127.0.0.1:5000
|
Open http://127.0.0.1:5000
|
||||||
|
|
||||||
|
## Production (WSGI)
|
||||||
|
|
||||||
|
This repo includes a `wsgi.py` entrypoint for production WSGI servers.
|
||||||
|
|
||||||
|
### Important (Windows)
|
||||||
|
|
||||||
|
If you try to run Gunicorn directly on Windows you will see an error like:
|
||||||
|
|
||||||
|
```
|
||||||
|
ModuleNotFoundError: No module named 'fcntl'
|
||||||
|
```
|
||||||
|
|
||||||
|
That’s expected: **Gunicorn is Unix-only**. On Windows, run the app via:
|
||||||
|
|
||||||
|
- **Docker** (recommended) so Gunicorn runs inside a Linux container, or
|
||||||
|
- **WSL2/Linux** (Gunicorn works), or
|
||||||
|
- use a Windows-native WSGI server (e.g. Waitress) instead of Gunicorn.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# gunicorn (Linux)
|
||||||
|
gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app
|
||||||
|
|
||||||
|
# uWSGI
|
||||||
|
uwsgi --http :8000 --wsgi-file wsgi.py --callable app
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: unlike `flask run`, WSGI servers typically don't auto-load `.env` / `.flaskenv`.
|
||||||
|
`wsgi.py` attempts to load `.env` (best-effort), but for real production you should set
|
||||||
|
environment variables via your process manager / secrets.
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
### Docker Compose (recommended)
|
||||||
|
|
||||||
|
This repo includes a `docker-compose.yml` for a one-command startup.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run in the background:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
Data persistence:
|
||||||
|
|
||||||
|
- SQLite DB is mounted to `./instance` on your host
|
||||||
|
- uploads are mounted to `./app/static/uploads` on your host
|
||||||
|
|
||||||
|
Build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t signage:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
Run (with persistent SQLite DB + uploads):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -p 8000:8000 \
|
||||||
|
-e SECRET_KEY="change-me" \
|
||||||
|
-v %cd%/instance:/app/instance \
|
||||||
|
-v %cd%/app/static/uploads:/app/app/static/uploads \
|
||||||
|
signage:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
PowerShell variant (sometimes volume path quoting is easier):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker run --rm -p 8000:8000 `
|
||||||
|
-e SECRET_KEY="change-me" `
|
||||||
|
-v "${PWD}/instance:/app/instance" `
|
||||||
|
-v "${PWD}/app/static/uploads:/app/app/static/uploads" `
|
||||||
|
signage:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open: http://127.0.0.1:8000
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- The container starts with Gunicorn using `wsgi:app`.
|
||||||
|
- You can override Gunicorn settings via env vars:
|
||||||
|
- `GUNICORN_WORKERS` (default: 2)
|
||||||
|
- `GUNICORN_BIND` (default: `0.0.0.0:8000`)
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- SQLite DB is stored at `instance/signage.sqlite`.
|
- SQLite DB is stored at `instance/signage.sqlite`.
|
||||||
@@ -121,3 +215,7 @@ If the reset email is not received:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
16
docker-compose.yml
Normal file
16
docker-compose.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
image: signage:latest
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
# Override in a .env file or your shell; this default is only for convenience.
|
||||||
|
SECRET_KEY: "change-me"
|
||||||
|
# Optional overrides (the Dockerfile already defaults these)
|
||||||
|
GUNICORN_WORKERS: "2"
|
||||||
|
GUNICORN_BIND: "0.0.0.0:8000"
|
||||||
|
volumes:
|
||||||
|
# Persist SQLite DB and uploads on the host
|
||||||
|
- ./instance:/app/instance
|
||||||
|
- ./app/static/uploads:/app/app/static/uploads
|
||||||
34
wsgi.py
Normal file
34
wsgi.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
"""WSGI entrypoint for production servers (gunicorn/uWSGI/etc.).
|
||||||
|
|
||||||
|
This file exposes a module-level WSGI callable named `app` (and `application`)
|
||||||
|
so common servers can run the project without relying on Flask's dev server.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app
|
||||||
|
uwsgi --http :8000 --wsgi-file wsgi.py --callable app
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
# `flask run` loads .env/.flaskenv automatically via python-dotenv.
|
||||||
|
# Production WSGI servers typically *don't*, so we best-effort load `.env` here.
|
||||||
|
# In real production, prefer setting environment variables via your process manager.
|
||||||
|
try:
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv(os.environ.get("DOTENV_PATH", ".env"), override=False)
|
||||||
|
except Exception:
|
||||||
|
# If python-dotenv isn't installed (or any other issue occurs), continue.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
from app import create_app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
|
||||||
|
# Some servers (and hosting platforms) look specifically for `application`.
|
||||||
|
application = app
|
||||||
Reference in New Issue
Block a user