Add Python/FastAPI + PostgreSQL backend (Phase 1)

- server/: FastAPI app with SQLAlchemy models for sops, work_packages, comments
- Endpoints for SOP/WP upsert+list+get+delete and comment create+list;
  /api/feedback kept as an alias so the existing client keeps working
- Portable across engines (PostgreSQL prod, SQLite dev fallback)
- requirements.txt, .env.example, and server/README.md (Postgres + systemd)
- NGINX now proxies /api/ to the API (replaces the Power Automate hop;
  comments persist to SQL)
- Rewrite DEPLOYMENT.md for the API + database architecture
- Add .gitignore for venv/.env/sqlite

Phase 2 (wire the client apps to the API) is next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 10:58:52 -07:00
parent 3d4402c88c
commit a33b777aca
10 changed files with 563 additions and 155 deletions

View File

@@ -1,17 +1,17 @@
# ─────────────────────────────────────────────────────────────────────────────
# Work Package Suite — NGINX site config
#
# Serves the static site and reverse-proxies feedback to a Power Automate
# "When an HTTP request is received" trigger. The browser only ever sees the
# same-origin path /api/feedback, so there is NO CORS and the secret trigger URL
# (with its sig= token) never reaches client code.
# Serves the static site and reverse-proxies /api/ to the Python API
# (FastAPI on 127.0.0.1:8000), which stores SOPs, Work Packages, and comments
# in PostgreSQL. Same-origin, so there is no CORS.
#
# Install:
# 1. Copy the project files to the web root (e.g. /var/www/wp-suite).
# 2. Put this file at /etc/nginx/conf.d/wp-suite.conf
# 2. Run the API as a service (see server/README.md) listening on :8000.
# 3. Put this file at /etc/nginx/conf.d/wp-suite.conf
# (or /etc/nginx/sites-available/ + symlink into sites-enabled/).
# 3. Replace server_name, the ssl_certificate paths, and the proxy_pass URL.
# 4. sudo nginx -t && sudo systemctl reload nginx
# 4. Replace server_name and the ssl_certificate paths.
# 5. sudo nginx -t && sudo systemctl reload nginx
# ─────────────────────────────────────────────────────────────────────────────
# Redirect plain HTTP to HTTPS
@@ -38,19 +38,15 @@ server {
try_files $uri $uri/ =404;
}
# ── Feedback proxy → Power Automate ──────────────────────────────────────
# Paste your real trigger URL into proxy_pass below, keeping the FULL query
# string (api-version, sp, sv, sig). A small body cap keeps this endpoint safe.
location = /api/feedback {
limit_except POST { deny all; } # only accept POST
client_max_body_size 256k;
proxy_pass https://prod-XX.westus.logic.azure.com/workflows/REPLACE_WORKFLOW_ID/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=REPLACE_SIGNATURE;
# ── API proxy → Python (FastAPI) ─────────────────────────────────────────
# All /api/ calls (SOPs, Work Packages, comments) go to the local API
# service. Keep the /api/ prefix — the API routes are defined under /api.
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_ssl_server_name on; # SNI — required for *.logic.azure.com
proxy_set_header Host prod-XX.westus.logic.azure.com; # <-- match your region host
proxy_set_header Content-Type application/json;
proxy_set_header Cookie ""; # don't leak site cookies upstream
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 5m; # WP documents can be larger
}
}