This commit is contained in:
Jamitz 2026-01-06 16:41:39 +00:00
parent fb6912b30f
commit 3d344b86ac
7 changed files with 183 additions and 13 deletions

32
backend/.dockerignore Normal file
View file

@ -0,0 +1,32 @@
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.venv/
venv/
ENV/
env/
*.db
*.sqlite
*.sqlite3
.pytest_cache/
.coverage
htmlcov/
.tox/
.mypy_cache/
.dmypy.json
dmypy.json
.vscode/
.idea/
*.log
.DS_Store
.env
.env.local
*.md
!README.md
.git/
.gitignore
Dockerfile
docker-compose*.yml
compose*.yaml

View file

@ -1,17 +1,35 @@
FROM python:3.11-slim
# ---- Builder stage ----
FROM python:3.12-slim AS builder
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# ---- Runtime stage ----
FROM python:3.12-slim
WORKDIR /app
# Copy installed packages from builder
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY app ./app
# Create data directory for SQLite and other persistent data
RUN mkdir -p /app/data
# Set environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
COPY ./app ./app
# Add healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()" || exit 1
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--port", "8000"]
# Run with production settings
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips", "*"]

View file

@ -10,7 +10,7 @@ app = FastAPI(title="Notes API")
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173",
"http://localhost:80",
"https://notes.fitzythe.dev",
], # Vite dev server
allow_credentials=True,

View file

@ -3,9 +3,41 @@ services:
build:
context: ./backend
container_name: fastnotes-api
environment:
- DATABASE_URL=${DATABASE_URL:-sqlite:///./data/notes.db}
- SECRET_KEY=${SECRET_KEY:-change-this-in-production}
- CORS_ORIGINS=${CORS_ORIGINS:-*}
# Internal only - accessed via nginx proxy
expose:
- "8000"
volumes:
- api_data:/app/data
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"python",
"-c",
"import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()",
]
interval: 30s
timeout: 10s
retries: 3
# ui:
# build:
# context: ./frontend
# args:
# VITE_API_URL: /api
ui:
build:
context: ./frontend
args:
# Frontend will use /api path (proxied by nginx)
VITE_API_URL: ${VITE_API_URL:-/api}
container_name: fastnotes-ui
ports:
- "80:80"
depends_on:
api:
condition: service_healthy
restart: unless-stopped
volumes:
api_data:

27
frontend/.dockerignore Normal file
View file

@ -0,0 +1,27 @@
node_modules/
dist/
build/
.git/
.gitignore
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.vscode/
.idea/
coverage/
.nyc_output/
*.md
!README.md
Dockerfile
docker-compose*.yml
compose*.yaml
.eslintcache
.cache
*.tsbuildinfo

View file

@ -6,13 +6,26 @@ WORKDIR /app
ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL
# Copy package files and install dependencies
COPY package*.json ./
RUN npm ci
RUN npm ci --prefer-offline --no-audit
# Copy source and build
COPY . .
RUN npm run build
# ---------- Runtime ----------
FROM nginx:stable-alpine AS runtime
# Copy custom nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy built assets
COPY --from=builder /app/dist /usr/share/nginx/html
# Add healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost/health || exit 1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

48
frontend/nginx.conf Normal file
View file

@ -0,0 +1,48 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Static files with caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# API proxy - routes /api requests to backend service
location /api {
proxy_pass http://api:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# SPA routing - serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}