odin 1dfa2c0a04 Add the profile page, and publish it as an embeddable panel
Developed in a separate session; committed here alongside the calendar work
that shares this repository's migration journal.

A person can now see their own Plex link, their Watch Now slots and their watch
history at /profile. The same panel is published in two further forms so that
accounts.sticknife.com on charon can carry it as one section of a wider
sticknife profile, next to the other services' sections.

  - watch_history (0024) records what has been played, keyed on the Plex
    history id so a re-sync cannot duplicate a row. Partial unique index,
    because that id is null for anything entered by hand.
  - plex_accounts.is_server_owner (0025) marks the one account whose viewing
    the server files under local account 1 rather than under its plex.tv id.
  - The embed carries its own layout, origin allowlist and a frame-height
    reporter, so the host page can size it without guessing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 15:42:17 +02:00

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

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:

npm run build && sudo systemctl restart ampelos.service

Contracts with ampelos-agent

The other half of the system is ampelos-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:

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.

S
Description
No description provided
Readme 8.5 MiB
Languages
TypeScript 97.2%
CSS 2.7%
JavaScript 0.1%