CI / ci (push) Canceled after 0s
Removed dockge from the app as it is not required due to the manager page picking up most of its abilities.
89 lines
3.7 KiB
Markdown
89 lines
3.7 KiB
Markdown
# NextChair Manager
|
|
|
|
Standalone platform-operator tool for provisioning and managing NextChair
|
|
business deployments on the shared multi-business VPS setup (see
|
|
`../stacks/`). Not part of the main app — a genuinely separate Next.js
|
|
app, own `package.json`, own build, own container.
|
|
|
|
## Why it's a separate app
|
|
|
|
An earlier attempt at this exact feature was built as routes inside the
|
|
main app (`src/app/manager/...`). It was reverted after repeated,
|
|
never-fully-resolved bugs: the main app's `src/proxy.ts`, root layout,
|
|
root page, and global `SessionProvider` all statically import Prisma /
|
|
Auth.js / business branding — code a Manager (which has no business
|
|
database of its own, by definition) kept transitively pulling in, causing
|
|
crashes in "Manager mode."
|
|
|
|
This app avoids that class of bug structurally rather than by careful
|
|
avoidance: it never imports anything from the main app's `src/` at all.
|
|
A few small pieces (bcrypt convention, the signed-cookie primitive) were
|
|
copied and adapted, not imported — see `src/lib/auth.ts` and
|
|
`src/lib/session.ts`.
|
|
|
|
## What it does
|
|
|
|
- Lists provisioned businesses and their running/stopped state.
|
|
- **Add Business**: collects a name/slug/domain, then generates secrets,
|
|
creates that business's database + user on the shared MySQL server,
|
|
writes its `stacks/<slug>/compose.yaml` + `.env` + Caddy site file,
|
|
reloads Caddy, starts the container, and runs its migrations. See
|
|
`src/lib/provisioning.ts` for the exact sequence.
|
|
- Per business: start / stop / restart / update (recreate the container
|
|
against the current shared image tag — not a source rebuild) / view
|
|
recent logs.
|
|
- Does **not** duplicate the `/install` wizard — after provisioning, the
|
|
operator is linked to `https://<domain>/install` to finish that
|
|
business's own setup.
|
|
|
|
## How auth works
|
|
|
|
A single dedicated platform-operator credential (`MANAGER_EMAIL` /
|
|
`MANAGER_PASSWORD_HASH`, bcrypt cost 12) — not tied to any one business's
|
|
own Super Administrator, since this tool spans every business's separate
|
|
database and has no "primary" one of its own. A signed (HMAC, not
|
|
encrypted) cookie holds the session; see `src/lib/session.ts`.
|
|
|
|
## The Docker-socket trust boundary
|
|
|
|
This container has the host's Docker socket mounted (`docker.sock`) so it
|
|
can run `docker compose`/`docker exec` against the host's dockerd — see
|
|
`src/lib/docker.ts`. Root inside this container plus that socket
|
|
effectively means root on the host. Consequences:
|
|
|
|
- It must stay behind the login above at all times — never expose it
|
|
without that gate, and don't reuse `MANAGER_PASSWORD_HASH` anywhere
|
|
else.
|
|
- `STACKS_DIR` must be bind-mounted at the *identical absolute path* on
|
|
both host and container (see `.env.example`) — a `docker compose -f
|
|
<path>` command run from inside this container is executed by the
|
|
*host's* dockerd, so the path only means the same file on both sides if
|
|
the mount doesn't change it.
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
cp manager/.env.example stacks/manager/.env # fill in real values
|
|
docker compose -f stacks/manager/compose.yaml --env-file stacks/manager/.env up -d --build
|
|
```
|
|
|
|
Requires `stacks/infra/compose.yaml` already running (shared MySQL,
|
|
Caddy, the `next-chair-edge` network). Add a Caddy site for this app the
|
|
same way as any business (see `stacks/infra/sites/.gitkeep`), pointed at
|
|
`next-chair-manager:3000`.
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
cd manager
|
|
npm install
|
|
cp .env.example .env.local
|
|
npm run dev
|
|
```
|
|
|
|
Requires network access to a real MySQL server and `docker`/`docker
|
|
compose` on the machine you run it from for the provisioning/stack
|
|
actions to actually work — the dashboard's read-only parts (listing
|
|
stacks) will also just show nothing if `STACKS_DIR` doesn't point at a
|
|
real directory yet.
|