# Data Entry App

Initial MVP implementation of the costing platform described in `CLAUDE.MD`.

## Structure

```text
backend/   FastAPI API, SQLAlchemy models, costing engine, seed data, tests
frontend/  SvelteKit UI scaffold for dashboard and core modules
deploy/    nginx config and Docker deployment assets
```

## Docker Alpha Deployment

This repo now includes a production-oriented Docker setup for an alpha release:

- `backend/Dockerfile`
- `frontend/Dockerfile`
- `docker-compose.yml`
- `docker-compose.alpha.yml`
- `deploy/nginx/clients.lean-101.conf`
- `deploy/nginx/clients.lean-101.proxy.conf`
- `.env.alpha.example`

The compose stack runs three services:

- `backend` on internal port `8000`
- `frontend` on internal port `3000`
- `nginx` on host port `8081` by default

`nginx` routes:

- `/api/*`, `/docs`, `/openapi.json`, `/health` to the FastAPI backend
- everything else to the Svelte frontend

### Browser vs server API base URLs

The frontend now supports:

- `PUBLIC_API_BASE_URL` for browser requests
- `INTERNAL_API_BASE_URL` for server-side SvelteKit requests inside Docker

This is important for a same-domain deployment such as `https://clients.lean-101.com.au`, where the browser should call the public domain while the frontend container should call the backend container directly.

### Example `/srv` deploy

```bash
mkdir -p /srv/lean101-clients
cd /srv/lean101-clients
git clone <this-repo> .
cp .env.alpha.example .env.alpha
docker compose --env-file .env.alpha up -d --build
```

This follows the same external pattern as your website container:

- one compose project under `/srv/lean101-clients`
- one host-facing port, `8081`, for the app
- your existing public reverse proxy forwards `clients.lean-101.com.au` to `127.0.0.1:8081`

If your server already has a host-level nginx handling domains and TLS, use `deploy/nginx/clients.lean-101.proxy.conf` as the upstream template and point the domain at `http://127.0.0.1:8081`.

## Production deployment (Postgres + Digital Ocean)

`docker-compose.production.yml` provisions a managed-style stack with a containerised Postgres 16 service replacing the SQLite file used in alpha. The PowerShell script `deploy/Deploy.ps1` drives both first-time bootstrap and incremental updates from a Windows workstation against a Digital Ocean droplet.

1. **One-time droplet prep**: install Docker Engine + the compose plugin (`apt install docker.io docker-compose-plugin` or the official Docker repo). Open inbound 80/443 on your reverse proxy and forward to `127.0.0.1:${CLIENTS_APP_PORT}` (default 8081).
2. **Local secrets**: copy the example env and fill in real secrets — strong `POSTGRES_PASSWORD`, `AUTH_SECRET`, `CLIENT_PASSWORD`, `ADMIN_PASSWORD`. The compose file refuses to start without them.

   ```powershell
   Copy-Item .env.production.example .env.production
   notepad .env.production
   ```

3. **First deploy** (clones the repo on the droplet, uploads the env file, brings the stack up):

   ```powershell
   ./deploy/Deploy.ps1 `
     -RemoteHost 203.0.113.10 `
     -Bootstrap `
     -RepoUrl git@github.com:ponzischeme89/data-entry-app.git `
     -Seed
   ```

4. **Subsequent updates** (the same script — pulls latest `main`, rebuilds, and rolls containers without touching the Postgres volume):

   ```powershell
   ./deploy/Deploy.ps1 -RemoteHost 203.0.113.10
   ```

   To deploy with password-based SSH auth instead of a key:

   ```powershell
   ./deploy/Deploy.ps1 -RemoteHost 203.0.113.10 -Password 'your-password'
   ```

   Password auth requires `sshpass` on your local `PATH`.

   Useful flags: `-Branch <name>` to deploy a feature branch, `-SkipBuild` for env-only changes, `-Seed` to re-run reference data seeding, `-Logs` to tail logs after the deploy, `-SshKey` to point at a specific private key, `-Password` for password-based SSH auth.

   If a release adds or changes database-backed workbook formula structures, deploy with `-Seed` so the server refreshes seeded reference/formula data after the backend starts. For the product-formula change, this is required so Postgres receives the new `product_ingredients` rows sourced from `input_data/1.xlsx`.

5. **Database**: the backend reads `DATABASE_URL`. The production compose file synthesises it as `postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}` so you only need to set the three `POSTGRES_*` vars. Override `DATABASE_URL` directly if you point at a managed Postgres (e.g. DigitalOcean managed databases).

The schema is auto-managed — `app/db/migrations.py` runs at backend startup and is idempotent across SQLite and Postgres. To migrate alpha SQLite data into the new Postgres instance, dump tables to CSV from the alpha container and import via `\copy` in `psql`; there is no automatic SQLite → Postgres path.

For this repo’s current schema, new tables such as `product_ingredients` are created automatically on backend startup in both SQLite and Postgres. Existing data refreshes still depend on seeding, so schema deployment and data deployment are separate concerns:

- backend startup creates missing tables/columns
- `-Seed` repopulates workbook-driven rows inside those tables

## Backend

Create a virtual environment, install dependencies, then run:

```bash
cd backend
pip install -e .
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
```

API docs will be available at `http://localhost:8000/docs` on the server itself, or `http://<server-ip>:8000/docs` from another machine on the same network.

Useful commands:

```bash
python -m app.seed
pytest
```

The backend defaults to SQLite for the prototype and can be switched with the
`DATABASE_URL` environment variable.

For local non-Docker runs, the default SQLite database is
`backend/data_entry_app.db` regardless of which directory you launch the
backend from. This avoids accidentally creating multiple local SQLite files
with different login data.

The internal login screen at `/` uses the seeded Hunter Stock Feeds users:

- `admin@hunterstockfeeds.com`
- `ops@hunterstockfeeds.com`
- `craig@hunterstockfeeds.com`

Unless you override `ADMIN_PASSWORD` before the first seed, those local
internal users are seeded with the default password `lean101-admin`.

### Backend logging

The backend now uses a shared console logger with a styled startup banner, concise request logs, and clean shutdown summaries.

Useful logging controls:

```bash
APP_ENV=production
LOG_LEVEL=INFO
LOG_VERBOSE=1
NO_COLOR=1
```

- `LOG_LEVEL` sets the base Python log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
- `LOG_VERBOSE=1` enables extra startup and route detail without changing normal request noise.
- `NO_COLOR=1` disables colours automatically for plain terminals, Docker log collection, or CI output.
- Colours are also disabled automatically when output is not a TTY.

Typical local development run:

```bash
cd backend
LOG_VERBOSE=1 uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
```

## Frontend

Install dependencies and start the dev server:

```bash
cd frontend
npm install
npm run dev
```

The frontend dev server now binds to `0.0.0.0`, so you can open it from another machine at `http://<server-ip>:5173`.

By default the browser will call the backend on the same hostname and port `8000`. For example, if you open the UI at `http://10.0.0.124:5173`, it will call `http://10.0.0.124:8000`.

Useful environment variables:

```bash
PUBLIC_API_PORT=8000
PUBLIC_API_BASE_URL=http://10.0.0.124:8000
CORS_ALLOW_ORIGINS=http://10.0.0.124:5173
```

Set `PUBLIC_API_BASE_URL` when the API is on a different machine or behind a different public URL. Set `CORS_ALLOW_ORIGINS` or `CORS_ALLOW_ORIGIN_REGEX` if you want to narrow backend CORS more tightly than the default private-network allowance.

## Delivered in this MVP

- Raw materials with versioned prices
- Mixes with ingredient rows and calculated cost per kg
- Products with transparent cost breakdowns
- Scenario runs with override support
- Power BI-style reporting endpoints
- SvelteKit dashboard and module pages aligned to the API contract
