Skip to content

feat(api/v2): ✨ Implement BuildTeam routes - #137

Open
kyanvde wants to merge 1 commit into
api-v2/claimsfrom
api-v2/buildteams
Open

feat(api/v2): ✨ Implement BuildTeam routes#137
kyanvde wants to merge 1 commit into
api-v2/claimsfrom
api-v2/buildteams

Conversation

@kyanvde

Copy link
Copy Markdown
Contributor

Closes#58.

Stacked on #136. Base is api-v2/claims, not api/v2, because PUT / queues a website revalidation and QueueService lands in that PR. Review the second commit for this change alone; GitHub retargets the base to api/v2 automatically once #136 merges.

Routes

RouteAuthNotes
GET /publicPaginated, biggest team first by default
GET /:teamIdoptional?slug=true, ?members=true, ?showcases=true
GET /modpackpublicEvery team, keyed by ID
GET /:teamId/modpackpublic?slug=true
PUT / and PUT /:teamIdteam

The root wildcard

This section is the one that could break every other one. GET /:teamId matches a single segment, so it matches /claims, /socials, /applications, /auth, /health, /version and /docs just as happily as a team ID. Express resolves that by registration order, which means:

  • BuildTeamsModule is registered last in AppModule, with a comment saying why.
  • modpack is declared before:teamId in the controller, and :teamId is declared last of all.
  • Swagger survives only because main.ts wires it onto the HTTP adapter before the controller routes are registered.

None of that is visible from the controller, so it is pinned by tests rather than left to a comment: buildteams.routes.spec.ts asserts that /v2/claims, /v2/socials, /v2/health, /v2/version, /v2/auth and PUT /v2/socials still reach their own controllers, and a new docs-routes.spec.ts builds the app the way main.ts does and asserts /v2/docs.json and /v2/docs.yaml still answer the OpenAPI document instead of a 404 from a team lookup.

Secrets

token (the client secret a team exchanges for an access token) and webhook are never selected into a public response. They are left out of the select rather than deleted from the result afterwards, so a column added to the model later has to be listed on purpose before it becomes public — and there is a test asserting neither appears in the select.

A team authenticated as itself does get its own webhook back on GET /:teamId and on PUT, since it needs to read back what it configured. token is not settable through PUT at all; the request is rejected as an unknown field.

Other notes

  • PUT queues REVALIDATE_WEBSITE for the team pages, which is what v1 did inline via rerenderFrontend. When the slug changed, the pages under the old slug are revalidated too, or the team keeps being served under a URL that no longer resolves.
  • A taken name or slug answers 409, not 500. Prisma's P2002 is caught in the service; unrelated database errors are re-thrown untouched.
  • slug is validated against ^[a-z0-9]+(?:-[a-z0-9]+)*$ — it becomes a public URL segment.
  • /modpack is an unpaginated map keyed by team ID, matching v1, because the modpack loads it once at startup and looks teams up by ID. ip is stored as one semicolon-separated string and served as a list, also matching v1.
  • The default sort is member count descending, which needs Prisma's relation-count ordering rather than a plain column; sortBy=members is mapped to it explicitly.

Testing

yarn ws api-v2 test — 33 suites, 282 tests, all passing. 50 are new:

  • buildteams.service.spec.ts — sorting (including the relation count), pagination, slug resolution, the optional embeds, webhook visibility in both directions, the modpack projections, the 409 on a taken slug, and that revalidation covers the old slug after a rename.
  • buildteams.routes.spec.ts — the wildcard collision cases above, plus the :teamId prefix rejecting another team, token being refused in a body, and an invalid slug being refused.
  • docs-routes.spec.ts — the Swagger routes.

yarn ws api-v2 build passes. yarn ws api-v2 lint reports the 6 pre-existing unbound-method errors documented in CLAUDE.md and nothing new.

🤖 Generated with Claude Code

Adds the root section: the public team list, a single team by ID or slug,
the two modpack projections, and an update scoped to the authenticated
team that queues a website revalidation for the pages it just made stale.
BuildTeamsController owns / and /:teamId, and that wildcard matches any
top level path, so BuildTeamsModule is registered last and the ordering
is pinned by tests, including one that the Swagger docs still resolve.
The token and webhook columns are never selected into a public response;
a team only reads back the webhook it configured itself.
Closes#58
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-project-automationgithub-project-automationBot moved this from Backlog to In Progress in @BuildTheEarth/web TrackerAug 29, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

feat(api/v2): ⭐ Implement BuildTeam routes

2 participants

@kyanvde@Nudelsuppe42