Give every plugin a resource page, and start the catalogue's move into the database - #277
Conversation
RESOURCE_HUB.md records what a SpigotMC-style hub for Dan's Plugins would need, which parts of SpigotMC are worth copying, and the decisions behind them: mirror GitHub releases rather than host files, keep reviews separate from discussion and from bug reports, and mark reviews from people whose servers actually run the plugin. The road map gains the six phases that follow from it, so the plan is visible on the site rather than only in the repository. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…file
V15 creates the plugins table and seeds it from pages/data/plugins.json,
and dpc-api serves it read-only at GET /api/v1/plugins and
/api/v1/plugins/{slug}. The slug is the id the catalogue already uses, so
existing guide URLs and likes.target_id rows keep resolving.
This is the first half of the move: the site still renders from the file,
which means the catalogue is written down twice for now, so
__tests__/pluginCatalogue.test.ts fails if the two ever disagree. Both the
file and that guard go away when the site switches to the API.
FlywayMigrationTest now executes the seed against real Postgres, which is
what actually checks the escaped apostrophes in "Dan's Essentials" and the
empty-string-to-NULL normalisation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The home page carries all sixteen plugins at once, so it can rank for none of them individually and a card has nowhere to put anything the grid can't fit. Each plugin now has an addressable page: icon, description, server count, latest release, like button, and download/guide/source/SpigotMC links, with bug reports routed to the plugin's issue tracker and feature ideas to the Dev Portal rather than collected on the page. The sitemap lists a resource URL per plugin. Server counts and release tags stay best-effort — an unreachable bStats or GitHub omits a chip rather than failing the page. Two extractions rather than second copies: GuideLikeButton becomes SelfLoadingLikeButton, taking its target as props, and the avatar's colour-from-title helper moves to utils/pluginAvatar.ts so a plugin looks the same on its card and on its page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ReviewThe full diff was read against the repo's own conventions. The shape of the change holds up: the two extractions ( No blocking defects were found. Six notes follow, five of them cosmetic. Findings
This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener). |
… yet The design doc asserted that a verified mark could be derived at read time from state dpc-api already holds, "exactly as SERVER_OWNER is derived". That is wrong, and would have been discovered mid-implementation: - the only inbound server data is faction sync, from Medieval Factions, and it carries no plugin identity — fifteen of sixteen plugins report nothing; - ApiKeyAuthFilter only checks that some valid key was presented, so it never resolves the owner and nothing reported is attributable to a user; - api_keys.server_name and factions.server_id are unrelated free text; - bStats is aggregate-only and cannot identify a reviewer. What is derivable today is SERVER_OWNER — "runs a server that syncs with DPC" — which is a different claim. The doc now states the gap, the three steps to close it (owner resolution, a plugin-side install report, then the derived mark), and that reviews ship without the mark until enough plugins report. Also: state exactly which fields the catalogue drift guard compares, rather than implying it compares everything. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Address the self-review on this PR: - The drift guard now compares github_url as well as slug and title. The download, source and bug-report links on a resource page are all derived from that URL, so a drift there is a broken link rather than a cosmetic mismatch. A new assertion pins which capture group is which, so a mis-numbered group cannot compare the wrong column. - servesTheOptionalFieldsAsNullWhenAbsent now asserts an explicit JSON null. doesNotExist() passes for an omitted key too, so it could not have noticed the response dropping those fields entirely -- a different shape from the one dpc-api/README.md documents. - Drop the blank lines that split each CHANGELOG section into two lists, attribute the plugins-table entry to #87 rather than #272, and stop hard-coding the plugin count in a comment in utils/sitemap.ts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two doc inaccuracies left by the verified-reviews correction and by the drift guard gaining a third field: - The road map's "Plugin Reviews" item still promised verified marks, which RESOURCE_HUB.md now says wait on an install signal dpc-api does not receive. The road map renders on the site, so it was promising something the design doc had just retracted. - RESOURCE_HUB.md described the drift guard as comparing two fields; it compares three now that github_url is included. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Turns the flat plugin grid into the first phase of a SpigotMC-style resource hub, and writes down where the rest of it is going.
Summary
RESOURCE_HUB.md— the design: what SpigotMC does that this site doesn't, which of it is worth copying, and the decisions behind it (mirror GitHub releases rather than host files; keep reviews separate from discussion and from bug reports; what it would take to mark a review as coming from a server that actually runs the plugin — see the correction below). The road map gains the six phases that follow from it./resources/[slug]— a page per plugin: icon, description, server count, latest release, like button, and download / guide / source / SpigotMC links. Bug reports go to the plugin's issue tracker and feature ideas to the Dev Portal, rather than being collected on the page. The sitemap lists a resource URL per plugin, so each plugin finally has a page that can rank for its own name instead of sharing the home page with fifteen others.V15+GET /api/v1/plugins[/{slug}]— the catalogue as a table, seeded frompages/data/plugins.jsonand served read-only. The site still renders from the file;__tests__/pluginCatalogue.test.tsfails if the two disagree on which plugins exist or what they are called (descriptions and icons are not compared — they are cosmetic, and the guard is deleted at switchover). Splitting the move this way keeps the schema change separate from changing what the home page renders from, so neither mistake can produce a blank catalogue. The second half is Allow editing the plugin cards data JSON file from the website #87.GuideLikeButtonbecomesSelfLoadingLikeButtontaking its target as props, and the avatar's colour-from-title helper moves toutils/pluginAvatar.tsso a plugin looks the same on its card and on its page.Changes to existing behaviour are limited to the plugin card: it gained a Details button and a linked title, GitHub dropped from the primary (contained) button to a secondary one now that Details is the primary action, and the actions row wraps instead of overflowing on a narrow card.
Correction included in this branch
The design doc originally claimed a verified-review mark could be derived at read time from state
dpc-apialready holds, "exactly asSERVER_OWNERis derived". A review of the branch found that to be wrong: the only inbound server data is faction sync from Medieval Factions, which carries no plugin identity;ApiKeyAuthFilteronly checks that some valid key was presented, so nothing reported is attributable to a user;api_keys.server_nameandfactions.server_idare unrelated free text; and bStats is aggregate-only. The last commit states the gap, the three steps to close it, and that reviews ship without the mark — now tracked as #278.Test plan
npm test— 219 passing, including new coverage forutils/resources.ts,releasesUrl,utils/pluginAvatar.ts, the resource page'sgetServerSideProps(not-found, missing param, empty-string normalisation, bStats down, no releases, both upstreams throwing), the catalogue drift guard, and the sitemap's new resource URLsnpm run lint— cleannpm run build— clean,/resources/[slug]builds as a server-rendered route./mvnw test -Dtest=PluginControllerTest— 6 passingFlywayMigrationTest— skipped locally (no Docker on this machine), but run in CI against real Postgres: the V15 seed applies, the escaped apostrophes in "Dan's Essentials" parse, and the empty-string-to-NULL normalisation holdsCloses #272
Part of #271
🤖 Generated with Claude Code
drafted by Claude on behalf of Daniel Stephenson