docs(#11): WOO_BASE_URL in compose, HTTPS-only, Settings read-only
Check in implementable deploy/docker-compose.yml, .env.example, and Caddy fragment. Store URL is env SoT (not a settings row). Application Password setup stays in DEPLOYMENT §5 (issue #5).
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
# Placeholders only. Copy to deploy/.env (chmod 600). Never commit real values.
|
||||||
|
# Generate SLAB_AES_KEY: openssl rand -base64 32
|
||||||
|
|
||||||
|
SLAB_AES_KEY=
|
||||||
|
APP_HOSTNAME=slab.example.local
|
||||||
|
FRONTEND_PORT=18080
|
||||||
|
FASTAPI_PORT=18081
|
||||||
|
|
||||||
|
# Plan of record: Woo store URL. HTTPS only. Not editable in Settings.
|
||||||
|
# Server must reject http:// at startup.
|
||||||
|
WOO_BASE_URL=https://www.whidbeywoodstore.com
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Merge into host Caddy. Replace {$APP_HOSTNAME} from inventory / .env.
|
||||||
|
# handle /api/* MUST come before the frontend catch-all.
|
||||||
|
|
||||||
|
{$APP_HOSTNAME} {
|
||||||
|
tls internal
|
||||||
|
handle /api/* {
|
||||||
|
reverse_proxy fastapi:8000
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
reverse_proxy frontend:80 {
|
||||||
|
header_up X-Real-IP {remote_host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ Do not assume a prior lab IP or hostname. See `docs/DEPLOYMENT.md`.
|
|||||||
| `APP_HOSTNAME` (HTTPS site name, e.g. `slab.example.local`) | |
|
| `APP_HOSTNAME` (HTTPS site name, e.g. `slab.example.local`) | |
|
||||||
| OS / Docker version | |
|
| OS / Docker version | |
|
||||||
| Notes | |
|
| Notes | |
|
||||||
|
| `WOO_BASE_URL` (`https://` store; not editable in app) | |
|
||||||
|
|
||||||
## Ports and collisions
|
## Ports and collisions
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Implementable compose for SlabUploader (docs/refactor-v2).
|
||||||
|
# From repo root:
|
||||||
|
# cp deploy/.env.example deploy/.env && chmod 600 deploy/.env
|
||||||
|
# docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d
|
||||||
|
# Bind loopback only. Caddy is the TLS edge. Services on the compose network
|
||||||
|
# are named `frontend` and `fastapi` (must match Caddyfile.fragment).
|
||||||
|
#
|
||||||
|
# WOO_BASE_URL is deploy-time SoT, HTTPS only. FastAPI must refuse http://
|
||||||
|
# at startup. Settings UI displays the URL read-only.
|
||||||
|
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: frontend/Dockerfile
|
||||||
|
ports: ["127.0.0.1:${FRONTEND_PORT:-18080}:80"]
|
||||||
|
depends_on: [fastapi]
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
fastapi:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: backend/Dockerfile
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
SLAB_AES_KEY: ${SLAB_AES_KEY}
|
||||||
|
WOO_BASE_URL: ${WOO_BASE_URL}
|
||||||
|
APP_HOSTNAME: ${APP_HOSTNAME:-}
|
||||||
|
# Woo Application Password + inference keys: AES-GCM in SQLite via Settings UI
|
||||||
|
volumes:
|
||||||
|
- slab_db:/data
|
||||||
|
- slab_images:/images
|
||||||
|
- ../prompts:/app/prompts:ro
|
||||||
|
ports: ["127.0.0.1:${FASTAPI_PORT:-18081}:8000"]
|
||||||
|
mem_limit: 2g
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
slab_db:
|
||||||
|
slab_images:
|
||||||
+2
-1
@@ -175,9 +175,10 @@ authoritative source for tag definitions; this table caches synced Woo tags.
|
|||||||
| `updated_at` | TEXT | |
|
| `updated_at` | TEXT | |
|
||||||
|
|
||||||
Keys (server settings; secrets encrypted at rest):
|
Keys (server settings; secrets encrypted at rest):
|
||||||
- `woo_base_url`, `woo_wp_username`, `woo_app_password`
|
- `woo_wp_username`, `woo_app_password`
|
||||||
(username + WordPress application password for a dedicated low-priv WP user;
|
(username + WordPress application password for a dedicated low-priv WP user;
|
||||||
not WooCommerce consumer keys)
|
not WooCommerce consumer keys)
|
||||||
|
- Store URL is **not** a settings row: `WOO_BASE_URL` env (compose), HTTPS only
|
||||||
- `inference_base_url`, `inference_api_key`, `inference_model`
|
- `inference_base_url`, `inference_api_key`, `inference_model`
|
||||||
- `inference_enabled`, `content_llm_enabled`
|
- `inference_enabled`, `content_llm_enabled`
|
||||||
- `brand_voice`, `geo_context`
|
- `brand_voice`, `geo_context`
|
||||||
|
|||||||
+25
-28
@@ -41,7 +41,7 @@ SlabUploader/
|
|||||||
│ ├── docker-compose.yml
|
│ ├── docker-compose.yml
|
||||||
│ ├── Caddyfile.fragment # slab site block (merged into host Caddy)
|
│ ├── Caddyfile.fragment # slab site block (merged into host Caddy)
|
||||||
│ ├── INVENTORY.md # filled at Gate B on the designated host
|
│ ├── INVENTORY.md # filled at Gate B on the designated host
|
||||||
│ ├── .env.example # SLAB_AES_KEY, APP_*, port vars (no real secrets)
|
│ ├── .env.example # SLAB_AES_KEY, APP_*, WOO_BASE_URL (https only)
|
||||||
│ └── backup.sh # sqlite .backup + images tar, with retention
|
│ └── backup.sh # sqlite .backup + images tar, with retention
|
||||||
├── frontend/ # SvelteKit app
|
├── frontend/ # SvelteKit app
|
||||||
└── backend/ # FastAPI app
|
└── backend/ # FastAPI app
|
||||||
@@ -58,33 +58,29 @@ SlabUploader/
|
|||||||
└── Dockerfile
|
└── Dockerfile
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. docker-compose.yml (target shape)
|
## 3. docker-compose.yml (checked in)
|
||||||
|
|
||||||
```yaml
|
Canonical file: `deploy/docker-compose.yml`. Placeholders: `deploy/.env.example`.
|
||||||
services:
|
From **repo root**:
|
||||||
frontend:
|
|
||||||
build: ./frontend
|
|
||||||
ports: ["127.0.0.1:${FRONTEND_PORT:-18080}:80"] # Caddy-only; not public
|
|
||||||
depends_on: [fastapi]
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
fastapi:
|
|
||||||
build: ./backend
|
|
||||||
environment:
|
|
||||||
SLAB_AES_KEY: ${SLAB_AES_KEY} # from .env (32-byte base64)
|
|
||||||
# no Woo/inference keys here — they live encrypted in SQLite (Settings UI)
|
|
||||||
volumes:
|
|
||||||
- slab_db:/data
|
|
||||||
- slab_images:/images
|
|
||||||
- ./prompts:/app/prompts:ro # prompt files, read-only mount
|
|
||||||
ports: ["127.0.0.1:${FASTAPI_PORT:-18081}:8000"] # Caddy-only
|
|
||||||
mem_limit: 2g # env convention: mem_limit preferred
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
slab_db:
|
|
||||||
slab_images:
|
|
||||||
```
|
```
|
||||||
|
cp deploy/.env.example deploy/.env && chmod 600 deploy/.env
|
||||||
|
# fill SLAB_AES_KEY, APP_HOSTNAME, WOO_BASE_URL (https:// only)
|
||||||
|
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Caddy site block: `deploy/Caddyfile.fragment` (merge into host Caddy). Compose
|
||||||
|
service names `frontend` and `fastapi` must match that fragment.
|
||||||
|
|
||||||
|
**`WOO_BASE_URL` (issue #11 — plan of record):**
|
||||||
|
|
||||||
|
- Set in `deploy/.env` and injected into the FastAPI container at start.
|
||||||
|
- **HTTPS only.** FastAPI must refuse missing / `http://` values at startup.
|
||||||
|
- Not a Settings input and **not** a SQLite settings row.
|
||||||
|
- Settings UI shows the URL as **read-only** text (which store this deploy hits).
|
||||||
|
- Change store = edit `.env` + recreate the fastapi container.
|
||||||
|
|
||||||
|
Woo Application Password stays out of compose (AES-GCM via Settings; issue #5).
|
||||||
|
|
||||||
Conventions (house rules):
|
Conventions (house rules):
|
||||||
|
|
||||||
@@ -95,8 +91,8 @@ Conventions (house rules):
|
|||||||
|
|
||||||
## 4. Caddy (fragment)
|
## 4. Caddy (fragment)
|
||||||
|
|
||||||
API must win over the frontend catch-all. Put `handle /api/*` **before** the frontend
|
Canonical file: `deploy/Caddyfile.fragment`. API must win over the frontend
|
||||||
`handle` block:
|
catch-all. Put `handle /api/*` **before** the frontend `handle` block:
|
||||||
|
|
||||||
```caddyfile
|
```caddyfile
|
||||||
{$APP_HOSTNAME} {
|
{$APP_HOSTNAME} {
|
||||||
@@ -121,7 +117,8 @@ API must win over the frontend catch-all. Put `handle /api/*` **before** the fro
|
|||||||
|
|
||||||
| Secret | Where | Notes |
|
| Secret | Where | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `SLAB_AES_KEY` | `.env` on host (mode 600) → compose env | 32-byte base64; NEVER in the repo |
|
| `SLAB_AES_KEY` | `deploy/.env` (mode 600) → compose env | 32-byte base64; NEVER in the repo |
|
||||||
|
| `WOO_BASE_URL` | `deploy/.env` → compose → FastAPI env | `https://` only; Settings **read-only** |
|
||||||
| Woo WP username + application password | SQLite `settings` (AES-GCM) via Settings UI | dedicated low-priv WP user; not consumer keys |
|
| Woo WP username + application password | SQLite `settings` (AES-GCM) via Settings UI | dedicated low-priv WP user; not consumer keys |
|
||||||
| Inference API key | SQLite `settings` (AES-GCM) via Settings UI | usually empty for local llama.cpp |
|
| Inference API key | SQLite `settings` (AES-GCM) via Settings UI | usually empty for local llama.cpp |
|
||||||
| `.env.example` | repo | placeholders only, committed |
|
| `.env.example` | repo | placeholders only, committed |
|
||||||
|
|||||||
+4
-2
@@ -120,7 +120,7 @@ the call returns. WooCommerce is the authoritative source for `feat-*` definitio
|
|||||||
|
|
||||||
### SettingsView (read; secrets masked)
|
### SettingsView (read; secrets masked)
|
||||||
```jsonc
|
```jsonc
|
||||||
{ "woo_base_url": "https://www.whidbeywoodstore.com",
|
{ "woo_base_url": "https://www.whidbeywoodstore.com", // WOO_BASE_URL env; read-only
|
||||||
"woo_wp_username": "slab-uploader", // non-secret identity; password never returned
|
"woo_wp_username": "slab-uploader", // non-secret identity; password never returned
|
||||||
"woo_credentials_configured": true,
|
"woo_credentials_configured": true,
|
||||||
"inference_base_url": "http://192.168.1.202:8080/v1",
|
"inference_base_url": "http://192.168.1.202:8080/v1",
|
||||||
@@ -209,6 +209,8 @@ Sheet/slider prefs are client localStorage only in POC (not in SettingsView).
|
|||||||
### Settings
|
### Settings
|
||||||
- `GET /api/v1/settings` → `200` `SettingsView`
|
- `GET /api/v1/settings` → `200` `SettingsView`
|
||||||
- `PUT /api/v1/settings` — Update settings. Body: partial `{ key: value }`.
|
- `PUT /api/v1/settings` — Update settings. Body: partial `{ key: value }`.
|
||||||
|
- `woo_base_url` is **not** writable (ignore or `422`). SoT is `WOO_BASE_URL`
|
||||||
|
env (HTTPS, compose). GET still returns it for read-only Settings text.
|
||||||
- Woo credential keys: `woo_wp_username` (plain) and `woo_app_password`
|
- Woo credential keys: `woo_wp_username` (plain) and `woo_app_password`
|
||||||
(write-only; AES-GCM at rest). Reject any payload that still sends
|
(write-only; AES-GCM at rest). Reject any payload that still sends
|
||||||
`woo_consumer_key` / `woo_consumer_secret`.
|
`woo_consumer_key` / `woo_consumer_secret`.
|
||||||
@@ -216,7 +218,7 @@ Sheet/slider prefs are client localStorage only in POC (not in SettingsView).
|
|||||||
never echoed).
|
never echoed).
|
||||||
- `POST /api/v1/settings/test-woo` → `200` `{ "ok": bool, "detail": "..." }`
|
- `POST /api/v1/settings/test-woo` → `200` `{ "ok": bool, "detail": "..." }`
|
||||||
- Uses stored username + application password over HTTPS Basic Auth against
|
- Uses stored username + application password over HTTPS Basic Auth against
|
||||||
the store REST v3 base. Fails closed if credentials missing or store HTTP.
|
`WOO_BASE_URL` REST v3. Fails closed if credentials missing or URL is not https.
|
||||||
- `POST /api/v1/settings/test-inference` → `200` `{ "ok": bool, "vision_capable": bool, "detail": "..." }`
|
- `POST /api/v1/settings/test-inference` → `200` `{ "ok": bool, "vision_capable": bool, "detail": "..." }`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user