2026-05-31 20:19:44 +12:00
2026-05-31 20:19:44 +12:00
2026-05-31 20:19:44 +12:00
2026-05-31 20:19:44 +12:00
2026-05-31 20:19:44 +12:00
2026-04-29 23:53:51 +12:00
2026-05-04 22:21:07 +12:00
2026-05-10 09:46:07 +12:00
2026-05-10 09:46:07 +12:00
2026-05-31 20:19:44 +12:00
2026-05-10 09:46:07 +12:00
2026-05-10 09:46:07 +12:00
2026-05-31 20:19:44 +12:00
2026-04-25 22:51:36 +12:00
2026-05-10 09:46:07 +12:00
2026-05-10 09:46:07 +12:00
2026-04-29 23:05:27 +12:00
2026-05-31 20:19:44 +12:00

Data Entry App

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

Structure

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

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.

    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):

    ./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):

    ./deploy/Deploy.ps1 -RemoteHost 203.0.113.10
    

    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.

    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 repos 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:

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:

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:

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:

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:

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:

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
S
Description
No description provided
Readme 2.8 MiB
Languages
Svelte 45.8%
Python 37.2%
TypeScript 9.8%
Shell 3.2%
PowerShell 2.3%
Other 1.6%