Demo business removal
CI / ci (push) Canceled after 0s

Removed the demo business as its easier to just provision one manually.
This commit is contained in:
Stefan Lazic committed 2026-09-06 19:01:41 +01:00
1 parent 217c0b0101
commit d7a8b15482
6 files changed
+32 -84

No files matched your search

+11 -11
View File
@@ -2,10 +2,10 @@
# stacks/infra/compose.yaml for the one-time setup this depends on
# (creating this business's database on the shared MySQL server) and
# stacks/<slug>/compose.yaml for how this file is used. Copy this to
# `stacks/<slug>/.env` (e.g. `stacks/demo-business/.env` — kept out of
# git, see .gitignore) per business and fill in real values. Nothing here
# is a real credential. Normally the management UI (manager/) generates
# this file for you — this template is for the by-hand setup path.
# `stacks/<slug>/.env` (kept out of git, see .gitignore) per business and
# fill in real values. Nothing here is a real credential. Normally the
# management UI (manager/) generates this file for you — this template is
# for the by-hand setup path.
#
# The app starts and runs a complete first-install flow with ONLY the
# "Required for basic startup" section below filled in — every other
@@ -16,12 +16,12 @@
# Manager-only bookkeeping (shown on the dashboard/edit screen, editable
# there) — the app itself never reads this; its own business name lives
# in the database, set via /install.
BUSINESS_NAME="Demo Business"
BUSINESS_NAME="Example Business"
# The public domain this business is served on — must match the .caddy
# site file's own domain (see stacks/infra/sites/.gitkeep) and that
# domain's real DNS (pointed at the VPS) before Caddy can issue a
# certificate for it.
DOMAIN="demo-business.example.com"
DOMAIN="example-business.example.com"
# The app reaches MySQL via the shared infra's service hostname
# `next-chair-mysql`, never `localhost` — see stacks/infra/compose.yaml.
@@ -31,14 +31,14 @@ DOMAIN="demo-business.example.com"
# tells the app what to connect as.
DATABASE_HOST="next-chair-mysql"
DATABASE_PORT="3306"
DATABASE_NAME="demo_business"
DATABASE_USER="demo_business"
DATABASE_NAME="example_business"
DATABASE_USER="example_business"
DATABASE_PASSWORD="replace-me"
# Generate with: npx auth secret
AUTH_SECRET="replace-me-with-a-32-byte-random-string"
# Auth.js needs to trust the host it's served from when not on localhost:3000.
AUTH_TRUST_HOST="true"
NEXT_PUBLIC_APP_URL="https://demo-business.example.com"
NEXT_PUBLIC_APP_URL="https://example-business.example.com"
# One-time setup token the installation wizard (/install) requires before
# it will create the first business/administrator — generate with:
# openssl rand -hex 32
@@ -51,7 +51,7 @@ CRON_SECRET="replace-me"
# Platform-level account with every Owner permission for THIS business.
# Not run automatically by this compose file (that's a Docker-entrypoint-
# only behaviour, see docker/entrypoint.sh) — run it manually, once:
# docker exec -it next-chair-demo-business npx tsx scripts/ensure-super-admin.ts
# docker exec -it next-chair-example-business npx tsx scripts/ensure-super-admin.ts
# Leave SUPER_ADMIN_EMAIL blank to skip entirely and use the /install
# wizard's own administrator account instead.
SUPER_ADMIN_EMAIL=""
@@ -68,7 +68,7 @@ AUTH_GOOGLE_SECRET=""
# OAuth client from AUTH_GOOGLE_ID/SECRET above (different scopes).
GOOGLE_CALENDAR_CLIENT_ID=""
GOOGLE_CALENDAR_CLIENT_SECRET=""
GOOGLE_CALENDAR_REDIRECT_URI="https://demo-business.example.com/api/staff/calendar/google/callback"
GOOGLE_CALENDAR_REDIRECT_URI="https://example-business.example.com/api/staff/calendar/google/callback"
# Encrypts stored refresh tokens at rest — generate with: openssl rand -hex 32
CALENDAR_TOKEN_ENCRYPTION_KEY=""
+3 -3
View File
@@ -6,9 +6,9 @@
#
# Only one instance of this ever runs (unlike the business image, which
# is built once and reused across every business — see
# stacks/demo-business/compose.yaml's own note) so a per-deploy `build:`
# in stacks/manager/compose.yaml, rather than a separately tagged image,
# is the simpler choice here.
# src/lib/compose-template.ts's own note) so a per-deploy `build:` in
# stacks/manager/compose.yaml, rather than a separately tagged image, is
# the simpler choice here.
FROM node:22-alpine AS deps
WORKDIR /app
+6 -5
View File
@@ -1,15 +1,16 @@
/**
* Generates the exact same shape as stacks/demo-business/compose.yaml
* and stacks/marios/compose.yaml (the two hand-written example stacks) —
* keep these in sync if that shape changes.
* The canonical definition of a business stack's compose.yaml shape —
* there's no separate hand-written example kept in the repo to stay in
* sync with; this generator is the single source of truth. See
* stacks/infra/compose.yaml's own header for the by-hand equivalent
* steps (creating the database, .env, Caddy site) this replaces.
*/
export function generateComposeYaml(slug: string): string {
const containerName = `next-chair-${slug}`;
const volumeName = `next-chair-media-${slug}`;
return `name: ${containerName}
# Generated by the management UI — see stacks/demo-business/compose.yaml
# for the hand-written original this mirrors.
# Generated by the management UI.
services:
${containerName}:
-55
View File
@@ -1,55 +0,0 @@
name: next-chair-demo-business
# One business's app container on the shared multi-business VPS setup —
# see stacks/infra/compose.yaml for the shared MySQL/Caddy this attaches
# to, and that file's header for the exact one-time setup steps
# (create this business's database, copy .env.business.example, add a
# Caddy site file) needed before this file can start.
#
# docker compose -f stacks/demo-business/compose.yaml --env-file stacks/demo-business/.env up -d
#
# `stacks/marios/compose.yaml` is the identical shape for another
# business — this is also the shape the management UI (manager/)
# generates for every new business it provisions.
#
# Uses the shared prebuilt image (see the repo root's README/Dockerfile —
# `docker build -t nextchair-app:latest .` once per code update) rather
# than building from source per business, so adding or updating a
# business is just starting/recreating a container, not a rebuild.
services:
next-chair-demo-business:
image: nextchair-app:latest
container_name: next-chair-demo-business
restart: unless-stopped
env_file:
- .env
volumes:
- next-chair-media-demo-business:/app/storage/media
healthcheck:
# No curl/wget in the node:22-alpine base image — a plain Node
# request avoids adding a package just for this. See
# src/app/api/health/route.ts: healthy as soon as the database is
# reachable, even before installation completes.
test:
[
"CMD",
"node",
"-e",
"require('http').get('http://localhost:3000/api/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))",
]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s
networks:
- next-chair-edge
volumes:
next-chair-media-demo-business:
name: next-chair-media-demo-business
networks:
next-chair-edge:
name: next-chair-edge
external: true
+10 -8
View File
@@ -6,11 +6,13 @@ name: next-chair-infra
# one shared schema — see step 1 below).
#
# Lives in stacks/infra/, one directory per stack (each with its own
# compose.yaml) — see stacks/demo-business/, stacks/marios/ for the same
# shape per business, and stacks/manager/ for the management UI's own
# stack. Deliberately separate from docker-compose.local.yaml at the
# repo root, which is the single-business local/Docker-Desktop setup and
# stays untouched by any of this.
# compose.yaml) — see stacks/manager/ for the management UI's own stack.
# Per-business stacks (stacks/<slug>/) are provisioned by the management
# UI itself (see manager/src/lib/compose-template.ts for the shape it
# generates) rather than kept as hand-written examples in this repo.
# Deliberately separate from docker-compose.local.yaml at the repo root,
# which is the single-business local/Docker-Desktop setup and stays
# untouched by any of this.
#
# Stack management/provisioning is handled by NextChair Manager (see
# manager/ at the repo root) — there is no separate stack-management UI
@@ -31,9 +33,9 @@ name: next-chair-infra
# initdb.d scripts only ever run once against an empty data
# directory — so this one step is manual, run once per business:
# docker exec -it next-chair-mysql mysql -uroot -p
# CREATE DATABASE demo_business CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# CREATE USER 'demo_business'@'%' IDENTIFIED BY 'choose-a-strong-password';
# GRANT ALL PRIVILEGES ON demo_business.* TO 'demo_business'@'%';
# CREATE DATABASE example_business CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# CREATE USER 'example_business'@'%' IDENTIFIED BY 'choose-a-strong-password';
# GRANT ALL PRIVILEGES ON example_business.* TO 'example_business'@'%';
# FLUSH PRIVILEGES;
# 2. Copy .env.business.example to stacks/<slug>/.env, fill it in with
# the credentials just created and that business's domain.
+2 -2
View File
@@ -2,8 +2,8 @@ Keeps this otherwise-empty directory tracked in git.
Each business gets its own <slug>.caddy file here, e.g.:
demo-business.example.com {
reverse_proxy next-chair-demo-business:3000
example-business.example.com {
reverse_proxy next-chair-example-business:3000
}
Generated by the management UI (manager/) when a business is added, or