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>
This commit is contained in:
odin
2026-08-17 15:42:17 +02:00
parent 3ccae54259
commit 1dfa2c0a04
30 changed files with 8119 additions and 233 deletions
+6
View File
@@ -174,6 +174,8 @@ export default async function Home({ searchParams }: PageProps) {
const kind = activeKind(resolvedSearchParams.tab);
const query = singleParam(resolvedSearchParams.q)?.trim() ?? "";
const collectionOnly = singleParam(resolvedSearchParams.collection) === "1";
// Set by the links on /profile, which point at a title rather than a tab.
const openExternalId = singleParam(resolvedSearchParams.open)?.trim() || null;
// The same flag the admin layout and the server actions check, rather than
// re-deriving it from the group list: a button that appears under one rule and
// an action that refuses under another is the worst version of this.
@@ -211,6 +213,9 @@ export default async function Home({ searchParams }: PageProps) {
</div>
</div>
<div className="flex flex-wrap items-center gap-2 text-sm">
<Link href="/profile" className="ampelos-ghost-button px-3 py-2 font-medium">
Profile
</Link>
{isAdmin && (
<Link href="/admin" className="ampelos-ghost-button px-3 py-2 font-medium">
Admin
@@ -274,6 +279,7 @@ export default async function Home({ searchParams }: PageProps) {
watchNowItems={watchNowItems}
slotCount={slotCount}
isAdmin={isAdmin}
openExternalId={openExternalId}
/>
</section>
</main>