# Redefined Designs One-of-a-kind item storefront. React + TypeScript + antd frontend (Vite), Express + TypeScript backend, Postgres, PayPal checkout, customer accounts with GDPR-style consent handling, and an authentik-gated admin panel. ## Project layout backend/ Express + TypeScript API, Postgres access, PayPal integration frontend/ React + TypeScript + antd storefront and admin UI (Vite) ## Prerequisites - Node.js 20+ - Docker (for a local, disposable Postgres instance) - npm ## Clone ```powershell git clone https://gitea.bermudalamb.synology.me/bermudalamb/redefined-designs.git cd redefined-designs ``` ## Running locally Commands below are shown for **PowerShell** (Windows). A bash equivalent is noted wherever the syntax differs. ### 1. Start a local Postgres instance The backend needs Postgres to talk to during local development. `backend/docker-compose.test.yml` spins up a throwaway, tmpfs-backed instance — no data persists between restarts, which is fine for local dev and tests. ```powershell cd backend npm run db:test:up ``` This starts Postgres on `localhost:55432`, database `redefined_test`. ### 2. Run migrations **PowerShell / bash (same command, cross-platform via Node):** cd backend $env:PGHOST="localhost"; $env:PGPORT="55432"; $env:PGUSER="redefined_test"; $env:PGPASSWORD="redefined_test"; $env:PGDATABASE="redefined_test" npm install npm run migrate:up This applies every migration in `backend/migrations/` in order, tracked in a `pgmigrations` table so re-running is always safe (already-applied migrations are skipped). To add a new migration: npm run migrate:create -- descriptive-name This generates a timestamped file in `backend/migrations/` with `exports.up`/`exports.down` stubs — fill in `pgm.sql(...)` for both directions. ### 3. Configure environment variables **PowerShell:** ```powershell $env:PGHOST = "localhost" $env:PGPORT = "55432" $env:PGUSER = "redefined_test" $env:PGPASSWORD = "redefined_test" $env:PGDATABASE = "redefined_test" $env:PORT = "3000" $env:DEMO_MODE = "true" $env:UPLOADS_DIR = "$env:TEMP\redefined-uploads" New-Item -ItemType Directory -Force -Path $env:UPLOADS_DIR | Out-Null ``` **bash:** ```bash export PGHOST=localhost export PGPORT=55432 export PGUSER=redefined_test export PGPASSWORD=redefined_test export PGDATABASE=redefined_test export PORT=3000 export DEMO_MODE=true export UPLOADS_DIR=/tmp/redefined-uploads mkdir -p /tmp/redefined-uploads ``` `DEMO_MODE=true` enables a "Buy Now (Demo)" button on the storefront that completes a purchase without needing real PayPal credentials — useful for local development and for the Playwright tests below. To exercise real PayPal checkout locally, also set `PAYPAL_CLIENT_ID`, `PAYPAL_CLIENT_SECRET`, and `PAYPAL_ENV=sandbox`. **Note (PowerShell):** environment variables set with `$env:` only last for the current terminal session/tab. If you close and reopen VS Code's terminal, you'll need to re-run step 3 before starting the backend again. ### 4. Run the backend ```powershell npm install npm run dev ``` Runs on `http://localhost:3000` with hot reload. ### 5. Run the frontend Open a **second** terminal tab/window (the backend needs to keep running in the first one): ```powershell cd frontend npm install npm run dev ``` Runs on `http://localhost:5173` and proxies `/api`, `/uploads`, `/webhooks` to the backend on port 3000. ### 6. Open it - Storefront: http://localhost:5173 - Admin: http://localhost:5173/admin Note: locally, `/admin` is directly reachable with no login gate — the authentik SSO protection only exists in the deployed environment (Nginx Proxy Manager + authentik forward-auth), not in local dev. ## Running tests ### Backend unit tests No database required. ```powershell cd backend npm run test:unit ``` ### Backend integration tests Exercises the real Express app against a real (disposable) Postgres instance via `supertest`. ```powershell cd backend npm run db:test:up npm run test:integration npm run db:test:down # when finished ``` ### Frontend Playwright e2e tests Needs the backend running against a database with the schema loaded (steps 1–4 above), since these tests drive real registration/login/purchase flows through a live API. ```powershell cd frontend npm install npx playwright install --with-deps # first time only — installs browser binaries npm run test:e2e ``` ## CI Gitea Actions runs two workflows on every push to `main` and on pull requests: - **SonarQube Analysis** (`.gitea/workflows/sonarqube.yml`) — static analysis scan, plus TypeScript build checks for both `backend` and `frontend` - **Tests** (`.gitea/workflows/tests.yml`) — backend unit tests, backend integration tests (against a disposable Postgres service container), and frontend Playwright e2e tests, each posting a results summary to the job's Summary tab in Gitea Actions ## Branching and commits - All changes go on a branch off `main`, named `feature/` or `fix/` — never commit directly to `main` - Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) syntax: `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `ci:`, `refactor:`, etc. - Branches are automatically deleted after a pull request is merged ## Production deployment Production runs as a single Docker image (multi-stage build — the frontend is built to static files and served directly by the backend), deployed via Portainer behind Nginx Proxy Manager, with authentik forward-auth gating `/admin`. That infrastructure is homelab-specific and documented separately outside this repo. The container applies pending migrations before starting the server, so deployed code can never be ahead of the database schema. A failed migration stops the container rather than letting it serve against a schema it doesn't match — check `docker logs` on the app container if it doesn't come up. ## QA environment `docker-compose.qa.yml` defines a disposable QA stack for reviewing merged-but-undeployed changes online. It runs alongside production on the same NAS with its own containers, database, volumes, and host port, and is started only when a review is needed. It differs from production deliberately: `DEMO_MODE=true` with no PayPal credentials (so checkout is exercisable but cannot reach live PayPal), no SMTP configuration (so it cannot email anyone), and `restart: "no"` (so a NAS reboot doesn't quietly bring it back up). ### One-time setup on the NAS **Build the image before creating the stack.** `redefined-designs:qa` exists only on the NAS — it is never pushed to a registry. If the stack is deployed first, Compose finds no local image, falls back to pulling from Docker Hub, and fails with a misleading `pull access denied ... repository does not exist or may require 'docker login'`. ```bash cd /volume1/docker/redefined-designs sudo docker build --no-cache -t redefined-designs:qa /volume1/docker/redefined-designs sudo docker images | grep redefined-designs # confirm the qa tag is present ``` For the same reason, leave Portainer's **"Pull latest image"** toggle **off** when deploying or redeploying this stack. The compose file sets `pull_policy: never` so a missing image reports itself as missing rather than as a registry authentication problem. Next, create the directory structure. These paths are deliberately **not** under `/volume1/configs/redefined-designs` — sharing production's Postgres directory would mean QA writing into production's database files. ```bash sudo mkdir -p /volume1/configs/redefined-designs-qa/postgres sudo mkdir -p /volume1/configs/redefined-designs-qa/uploads # The official postgres image runs as uid/gid 999. Without this the DB # container exits immediately with a data-directory permissions error. sudo chown -R 999:999 /volume1/configs/redefined-designs-qa/postgres sudo chmod 700 /volume1/configs/redefined-designs-qa/postgres # Uploads are written by the app container, which runs as root. sudo chown -R root:root /volume1/configs/redefined-designs-qa/uploads sudo chmod 755 /volume1/configs/redefined-designs-qa/uploads # Confirm the two environments are separate before going further. ls -la /volume1/configs/redefined-designs-qa/ ls -la /volume1/configs/redefined-designs/ ``` Then, in Portainer, create a stack **named `redefined-designs-qa`** from `docker-compose.qa.yml`, with one environment variable `QA_DB_PASSWORD`. The stack name matters: it becomes the compose project name, and reusing production's name would make compose reconcile the two stacks against each other and remove the production containers. Finally, add an Nginx Proxy Manager host for `qa-redefined-designs` pointing at the NAS on port `32751`, with authentik forward-auth on `location /` — the whole site, not just `/admin`, so nothing unreleased is publicly reachable. ### Reviewing a change ```bash cd /volume1/docker/redefined-designs gitc() { sudo docker run --rm -it -v /volume1/docker/redefined-designs:/repo -v ~/.gitconfig-docker/.gitconfig:/root/.gitconfig -w /repo alpine/git "$@"; } gitc pull origin main gitc log --oneline -3 # Rebuild the QA image before restarting the stack — the stack does not build # anything itself, and will otherwise run whatever was last tagged :qa. sudo docker build --no-cache -t redefined-designs:qa /volume1/docker/redefined-designs # start the redefined-designs-qa stack in Portainer # Migrations run as the container starts — confirm before reviewing. sudo docker logs redefined-designs-qa-syn | head -30 ``` Stop the stack in Portainer when finished. ### Resetting QA data QA data is disposable. To start from an empty database, stop the stack first, then: ```bash # Check the path before running this. It must contain -qa. sudo rm -rf /volume1/configs/redefined-designs-qa/postgres/pgdata ``` Restarting the stack recreates the database and re-runs every migration from scratch.