# ampelos-dashboard

The web face of Ampelos: inventory, calendar, requests, the link-verification
panel, and the admin surfaces that drive the rest of the system. Next.js 16 App
Router, Postgres via Drizzle, Authentik for sign-in.

It is also the **owner of the database schema**. See "Contracts" below, because
that ownership is now split across two repositories.

## Running it

```bash
npm install
cp .env.example .env.local      # then fill it in
npm run db:migrate
npm run dev
```

Production is a systemd unit (`ampelos.service`) running `next start` on :3000
behind the reverse proxy for ampelos.sticknife.com.

Rebuilding in place is a single step, because `next build` replaces the build
that `next start` is currently serving — do them together or the site is broken
in between:

```bash
npm run build && sudo systemctl restart ampelos.service
```

## Contracts with ampelos-agent

The other half of the system is [ampelos-agent][agent]: acquisition, the
filesystem maintenance jobs, and the satellites reporting in from other hosts.
It does not import anything from this repository, and this repository does not
import anything from it. They meet in exactly two places, and both are easy to
break by accident now that they are versioned separately.

### 1. The database schema — owned here

`src/db/schema/` and `src/db/migrations/` are the definition. The agent speaks
raw SQL against the same tables and holds no copy of the schema.

So a migration here can break the agent silently. The agent carries
`scripts/check-schema.mjs`, a snapshot of the 24 tables and the load-bearing
columns it reads; run it there after any migration that renames or removes
something:

```bash
cd ../ampelos-agent && npm run check:schema
```

Adding tables and columns is always safe. Renaming and dropping are not.

### 2. Five HTTP endpoints

Three the agent calls here, and they are how the satellites report in:

| Endpoint | Caller | Breaks if this app is down |
| --- | --- | --- |
| `POST /api/agents/heartbeat` | `ampelos-herald.sh` on each host | Heartbeats stop, so the maintenance container never mounts edda, so backup and archive jobs skip themselves |
| `POST /api/agents/status` | `ampelos-mountd.sh` | Mount state stops being reported |
| `POST /api/agents/corrupt-files` | `ampelos-transcoder.mjs` | Unreadable files are logged locally instead |

Two this app calls there, via `AMPELOS_INDEXER_URL` (see `src/lib/indexer.ts`):

| Call | Used by | Breaks if the indexer is down |
| --- | --- | --- |
| search releases | `admin/search-actions.ts` | The manual search panel returns an error |
| grab release | `admin/search-actions.ts` | Manual grabs fail; the automatic loop is unaffected |

Both directions authenticate with `AMPELOS_AGENT_TOKEN`, which must be the same
value in both repositories' environments and belongs in neither's git history.

### What still works when the other half is stopped

Everything here reads from Postgres, so with the agent stopped the dashboard
renders normally — inventory, calendar, verification — it simply goes stale, and
manual search and grab fail. With the dashboard stopped, the agent keeps
downloading, importing, scanning and reaping; only the edda-mounted tiers pause,
for the heartbeat reason above.

## Layout

- `src/app/(admin)/` — the admin surfaces. Server Actions are reachable by
  direct POST, so every one of them checks the session itself.
- `src/db/` — schema, migrations, client.
- `media/` — source brand art. `public/` holds what the app actually serves.

The TrueNAS health-broadcast timer used to live here under `deploy/`. It posts
to the scan listener on :3427, which is an agent script, so it moved to
`ampelos-agent/deploy/truenas/` with the thing it talks to.

## Working on it

Read `AGENTS.md` first. Next.js 16 differs from what a model is likely to
remember, and the local documentation in `node_modules/next/dist/docs/` is the
authority.

[agent]: ../ampelos-agent
