From 12c404696577264cae9d001cb06611e573917181 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Mon, 18 May 2026 16:19:04 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20move=20CMS=20Wave=201=20W1.3-W1.6=20and?= =?UTF-8?q?=20PLAN.md=20=C2=A72.4=20to=20COMPLETED.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMS-PLAN.md | 4 ---- COMPLETED.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-- PLAN.md | 12 ------------ 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/CMS-PLAN.md b/CMS-PLAN.md index d8681a3..6377f45 100644 --- a/CMS-PLAN.md +++ b/CMS-PLAN.md @@ -245,10 +245,6 @@ Themes, not dates. The order between waves is sequential (each depends on its pr - **W1.0 `DeepDrftContext` Postgres migration.** Rewrite all existing EF Core migrations from SQLite to PostgreSQL. Update the `DeepDrftWeb` and `DeepDrftCli` connection strings in config. Migrate any existing data from `../Database/deepdrft.db` to Postgres. Verify the existing `api/track/page` and `api/track/{id}` endpoints function against the new backend. This is a prerequisite for W1.2 (which also runs migrations for AuthDbContext against the same Postgres instance). - **W1.1 `DeepDrftCms` RCL skeleton.** Project created, added to solution, referenced from `DeepDrftWeb`. Empty `Pages/Cms/Index.razor` mounted at `/cms` returning a "CMS — under construction" placeholder, proving the mount works. - **W1.2 AuthBlocks integration + login.** Reference `Cerebellum.AuthBlocks`, `Cerebellum.AuthBlocks.Web`, `Cerebellum.AuthBlocks.Models` from `DeepDrftWeb`; reference `Cerebellum.AuthBlocks.Web` from `DeepDrftWeb.Client`. Call `AddAuthBlocks(...)` in `Program.cs` with JWT secret/issuer/audience, Mailtrap email connection, Postgres connection string, and `AdminUserSettings` from `environment/authblocks.json`. Call `await app.Services.UseAuthBlocksStartupAsync()` post-build. Call `app.MapAuthBlocks()` to mount `/api/auth/*` routes. Add the `AuthBlocksWeb` assembly to `AddAdditionalAssemblies` so the bundled `/account/login` and `/account/logout` pages resolve. In `DeepDrftWeb.Client.Startup`, call `AuthBlocksWeb.Client.Startup.ConfigureServices(builder.Services)` for the prerender→WASM auth-state bridge. Add `CreatedByUserId : long?` column to `TrackEntity` via a nullable migration. Provision local Postgres (docker-compose) and document the dev setup. Verify: anonymous visit to `/cms/anything` redirects to `/account/login`; authenticated `Admin` lands successfully. -- **W1.3 CMS track list.** `/cms/tracks` consuming the same `GET api/track/page` endpoint as the public gallery. Different rendering (table with admin affordances), same VM. No new SQL endpoint. -- **W1.4 CMS upload endpoint + add page.** New `POST api/cms/track` on `DeepDrftWeb` (auth-gated, see §5 for the transport decision). `/cms/tracks/new` page wires `InputFile` to the endpoint. Note: Option B is confirmed — this requires a new `POST api/track/upload` endpoint on `DeepDrftContent` (raw WAV in, unpersisted `TrackEntity` out) in addition to the CMS page and controller. -- **W1.5 CMS delete endpoint + delete UI.** New `DELETE api/cms/track/{id}` on `DeepDrftWeb`. Removes the SQL row and the vault entry; logs orphans if vault delete fails after SQL delete succeeds. Delete button + confirmation in the list and detail pages. -- **W1.6 CMS edit endpoint + edit page.** New `PUT api/cms/track/{id}` (metadata only — no binary replacement in Wave 1). `/cms/tracks/{id}` page. ### Wave 2 — Operations the CLI never had diff --git a/COMPLETED.md b/COMPLETED.md index 98d9d66..f767c44 100644 --- a/COMPLETED.md +++ b/COMPLETED.md @@ -1,8 +1,59 @@ # COMPLETED.md — DeepDrftHome -Archive of items that have moved out of `PLAN.md`. Per `CONTEXT.md §6`, completed `PLAN.md` items are moved here rather than deleted. Each entry preserves the original "What / Why / Shape" body so this file reads as a decision record, not just an outcome list. +Archive of items that have moved out of `PLAN.md` and `CMS-PLAN.md`. Per `CONTEXT.md §6`, completed items are moved here rather than deleted. Each entry preserves the original "What / Why / Shape" body so this file reads as a decision record, not just an outcome list. -Newest entries at the top. Group by phase header (mirroring `PLAN.md` themes) when there are enough entries to warrant it. +Newest entries at the top. Group by phase/wave header (mirroring `PLAN.md` / `CMS-PLAN.md` themes) when there are enough entries to warrant it. + +--- + +## CMS Wave 1 — Auth + scaffolding + parity + +**Status:** All sub-items landed on 2026-05-18. + +Goal was: A logged-in collective member can do everything the CLI does today, from a browser. + +### W1.3 CMS track list + +**Landed in CMS Wave 3.** + +`/cms/tracks` consuming the same `GET api/track/page` endpoint as the public gallery. Different rendering (table with admin affordances), same VM. No new SQL endpoint. + +### W1.4 CMS upload endpoint + add page + +**Landed in CMS Wave 3.** + +New `POST api/cms/track` on `DeepDrftWeb` (auth-gated, see §5 for the transport decision). `/cms/tracks/new` page wires `InputFile` to the endpoint. Note: Option B is confirmed — this requires a new `POST api/track/upload` endpoint on `DeepDrftContent` (raw WAV in, unpersisted `TrackEntity` out) in addition to the CMS page and controller. + +### W1.5 CMS delete endpoint + delete UI + +**Landed in CMS Wave 3.** + +New `DELETE api/cms/track/{id}` on `DeepDrftWeb`. Removes the SQL row and the vault entry; logs orphans if vault delete fails after SQL delete succeeds. Delete button + confirmation in the list and detail pages. + +### W1.6 CMS edit endpoint + edit page + +**Landed in CMS Wave 3.** + +New `PUT api/cms/track/{id}` (metadata only — no binary replacement in Wave 1). `/cms/tracks/{id}` page. + +--- + +## Phase 2 — Product surface: gallery, browsing, ingestion + +### 2.4 Web-side track upload + +**Landed in CMS Wave 1 (subsumed by `CMS-PLAN.md`).** + +The CLI is the only producer of tracks today. A web upload UI would pair with `TrackService.AddTrackFromWavAsync` and the existing `PUT api/track/{id}` (already `[ApiKeyAuthorize]`-protected). + +- **Why it matters:** Lowers the barrier to adding content. The collective can publish without shell access to the host. +- **Shape:** + - New page or modal on the web client, drag-and-drop file input. + - Upload streams to a `POST` endpoint on `DeepDrftWeb` (not `DeepDrftContent` — the web host orchestrates the dual-write, then forwards bytes to content with the API key it already holds). + - Authentication: this is the first user-facing action that needs to be gated. A new question — see open question below. +- **Prerequisite:** **Authentication model for the web side**. Currently the site has no user concept. Cookie-with-shared-password? OAuth? Per-collective-member account? Decide before building the UI. +- **Open question:** Same as above. This may also bring forward a wider session/identity decision that other features (favourites, listening history) will need eventually. +- **Constraint:** Today's dual-write has no compensating rollback — if content-side succeeds and SQL-side fails, the audio is orphaned in the vault. The CLI inherits this; pushing this onto a web upload increases the rate at which orphans can occur. A simple `DeadLetterLog` of orphaned `entryKey`s (suggested in the audit) becomes more pressing once the web upload exists. --- diff --git a/PLAN.md b/PLAN.md index f10f4b6..e60f5e2 100644 --- a/PLAN.md +++ b/PLAN.md @@ -116,18 +116,6 @@ These follow from `CONTEXT.md §5`. Direction is strongly implied but no specifi - **Shape:** Same extension to `GetPaged` as 2.2. UI is a debounced text input bound to the VM's filter property. EF Core translates `Contains` to SQLite `LIKE`. - **Prerequisite:** Fold into 2.2 if both are being done — the same `GetPaged` extension serves both. Doing them separately doubles the API churn. -### 2.4 Web-side track upload - -- **What:** The CLI is the only producer of tracks today. A web upload UI would pair with `TrackService.AddTrackFromWavAsync` and the existing `PUT api/track/{id}` (already `[ApiKeyAuthorize]`-protected). -- **Why it matters:** Lowers the barrier to adding content. The collective can publish without shell access to the host. -- **Shape:** - - New page or modal on the web client, drag-and-drop file input. - - Upload streams to a `POST` endpoint on `DeepDrftWeb` (not `DeepDrftContent` — the web host orchestrates the dual-write, then forwards bytes to content with the API key it already holds). - - Authentication: this is the first user-facing action that needs to be gated. A new question — see open question below. -- **Prerequisite:** **Authentication model for the web side**. Currently the site has no user concept. Cookie-with-shared-password? OAuth? Per-collective-member account? Decide before building the UI. -- **Open question:** Same as above. This may also bring forward a wider session/identity decision that other features (favourites, listening history) will need eventually. -- **Constraint:** Today's dual-write has no compensating rollback — if content-side succeeds and SQL-side fails, the audio is orphaned in the vault. The CLI inherits this; pushing this onto a web upload increases the rate at which orphans can occur. A simple `DeadLetterLog` of orphaned `entryKey`s (suggested in the audit) becomes more pressing once the web upload exists. - --- ## Phase 3 — New content kinds