Skip to content

Webhook subscriptions - #122

Merged
owens1127 merged 23 commits into
mainfrom
webhook-subscriptions
Apr 24, 2026
Merged

Webhook subscriptions#122
owens1127 merged 23 commits into
mainfrom
webhook-subscriptions

Conversation

@owens1127

Copy link
Copy Markdown
Contributor

No description provided.

Fix ESLint no-unsafe-call on getInstancePlayerInfo in instances test (avoid .catch(console.error) widening types).

Made-with: Cursor
@owens1127
owens1127 marked this pull request as ready for review April 23, 2026 03:33
Copilot AI review requested due to automatic review settings April 23, 2026 03:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces Discord webhook subscription management endpoints (create/update/delete/status) backed by Postgres, adds Discord invocation context auth handling, and extends player search with pagination via offset.

Changes:

  • Add Discord webhook subscription service + /subscriptions/discord/webhooks routes and OpenAPI schema components.
  • Add auth context plumbing (req.auth, req.discord) and refactor protected-resource/admin checks to use parsed auth context.
  • Add offset pagination to player search query/service and update related tests/docs/config.

Reviewed changes

Copilot reviewed 45 out of 46 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tsconfig.json Includes scripts/**/*.ts in TS compilation.
src/services/subscriptions/discord-webhooks.ts Implements Discord webhook destination/rule upsert, status, and deactivation logic.
src/services/subscriptions/discord-webhooks.test.ts Adds unit tests for webhook registration/upsert/status/deactivation behaviors.
src/services/search/player-search.ts Adds offset support to SQL query and membershipId shortcut behavior.
src/services/search/player-search.test.ts Updates service tests to pass offset and minor assertion tweaks.
src/services/reporting/standing.test.ts Removes non-null assertions in expectations.
src/services/player-instances/instances.test.ts Adjusts non-null assertions and error handling in tests.
src/services/manifest/urls.ts Removes non-null assertion on result[activityId].
src/services/instance/instance.test.ts Removes non-null assertion when reading fixture insert result.
src/schema/errors/ErrorCode.ts Adds InvalidDiscordAuthError error code.
src/schema/components/DiscordSubscriptionWebhook.ts Adds Zod/OpenAPI schemas for Discord webhook subscription APIs.
src/routes/subscriptions/index.ts Adds /subscriptions router.
src/routes/subscriptions/discord/webhooks.ts Adds GET/PUT/DELETE routes for Discord webhook subscription management.
src/routes/subscriptions/discord/index.ts Wires Discord subscription webhook routes under /subscriptions/discord.
src/routes/player/search.ts Adds offset query param and includes it in response params.
src/routes/player/search.test.ts Updates route tests to remove non-null assertions.
src/routes/player/membershipId/teammates.ts Switches protected-resource check to use req.auth.
src/routes/player/membershipId/profile.ts Switches protected-resource check to use req.auth.
src/routes/player/membershipId/instances.ts Switches protected-resource check to use req.auth.
src/routes/player/membershipId/history.ts Switches protected-resource check to use req.auth.
src/routes/instance.ts Refactors control-flow formatting while preserving behavior.
src/routes/index.ts Mounts new /subscriptions router.
src/routes/authorize/user.test.ts Removes non-null assertion on process.env.JWT_SECRET in test.
src/routes/authorize/admin.test.ts Removes non-null assertion on process.env.JWT_SECRET in test.
src/integrations/postgres/transactional.ts Simplifies queryRow return (`rows[0]
src/integrations/postgres/reader.ts Simplifies queryRow return (`rows[0]
src/integrations/discord/context-jwt.ts Adds JWT sign/verify helpers for Discord invocation context.
src/index.ts Adds attachUserAuth and attachDiscordContext middleware to app pipeline.
src/core/RaidHubRouterTypes.ts Extends handler request type with auth and discord contexts.
src/core/RaidHubRoute.ts Adds $mock support for auth via authFromHeaders.
src/auth/user-context.ts Adds Bearer JWT parsing + Express middleware attaching req.auth.
src/auth/user-context.test.ts Adds tests for authFromHeaders.
src/auth/protected-resource.ts Refactors protected-resource checks to use parsed auth context.
src/auth/express-discord-context.ts Adds Express type augmentation for req.discord.
src/auth/express-auth-context.ts Adds Express type augmentation for req.auth.
src/auth/discord-context.ts Adds Express middleware attaching req.discord from Authorization: Discord <jwt>.
src/auth/api-keys.ts Removes non-null assertion from API_KEYS_PATH use and updates linting scope.
src/auth/admin.ts Refactors admin protection to use req.auth?.isAdmin.
src/auth/admin.test.ts Updates middleware chain to include attachUserAuth and sets a test JWT secret fallback.
package.json Switches typecheck to bunx tsc, updates eslint config to type-checked rules, and adjusts lint/lint-staged patterns.
open-api/openapi.json Updates generated OpenAPI for new subscription endpoints and search offset.
example.env Documents API_KEYS_PATH and adds Discord integration env vars.
api-keys.example.json Adds example API key file for local/prod usage.
README.md Updates local setup instructions for API keys and PROD behavior.
.github/raidhub-services-version.env Bumps referenced RaidHub-Services commit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/routes/subscriptions/discord/webhooks.ts
Comment thread src/auth/api-keys.ts Outdated
Comment thread src/services/subscriptions/discord-webhooks.ts Outdated
Comment thread src/services/subscriptions/discord-webhooks.ts Outdated
Comment thread src/schema/components/DiscordSubscriptionWebhook.ts
Comment thread src/schema/errors/ErrorCode.ts
Comment thread src/auth/discord-context.ts
Address PR review feedback by registering InvalidDiscordAuthError, documenting 401 responses for Discord webhook routes, hardening Discord auth header parsing, adding API key path fallback, and reconciling/de-duping webhook targets with bounded input size.

Made-with: Cursor
Refresh generated OpenAPI output so docs/checks reflect the new Discord webhook error contracts and schema updates.

Made-with: Cursor
Use an explicit unknown-to-request cast in the mock request setup so tsc accepts the test harness while preserving the header-array coverage.

Made-with: Cursor
Match repository formatting expectations for discord-context middleware so the format check passes in CI.

Made-with: Cursor
@owens1127

Copy link
Copy Markdown
Contributor Author

@sentry review

Comment thread src/services/subscriptions/discord-webhooks.ts Outdated
Comment thread src/services/subscriptions/discord-webhooks.ts Outdated
Delete the Discord webhook if the Postgres transaction fails after
create, since the API cannot run inside a DB transaction. Run
destination reactivation and rule updates in a single transaction
during upsert. Extract shared update path for reuse.

Made-with: Cursor
@owens1127

Copy link
Copy Markdown
Contributor Author

Addressed the two Sentry review items in c146e5f:

  1. Orphan webhooks (CRITICAL): Discord’s create call cannot run inside a Postgres transaction. Instead, if the DB transaction fails after a successful create, we best-effort DELETE /webhooks/{id} so retries do not accumulate stray webhooks. Added a unit test that asserts POST then DELETE on rollback.

  2. Reactivation vs rules (MEDIUM): upsertDiscordWebhook now reactivates the destination and runs the guild/rule updates in a single pgAdmin.transaction, so a rule update failure rolls back reactivation as well.

Also factored updateDiscordWebhookInDb so updateDiscordWebhook and the upsert path share the same update logic.

Comment on lines +358 to +368
try {
createdWebhook = await createDiscordWebhook({
channelId: input.channelId,
name: input.name
})
const webhookUrl = validateDiscordWebhookUrl(createdWebhook.url)
const requireFresh = input.filters?.requireFresh ?? false
const requireCompleted = input.filters?.requireCompleted ?? false
const playerMembershipIds = normalizeTargetIds(input.targets?.playerMembershipIds)
const clanGroupIds = normalizeTargetIds(input.targets?.clanGroupIds)

This comment was marked as outdated.

Sentry follow-up: registerDiscordWebhook always created a new channel webhook
but could leave the previous Discord webhook orphaned when a row already
existed (e.g. direct service calls). Look up stored webhook_id, best-effort
DELETE, then create and persist the new webhook. Tests cover the DELETE+POST
sequence and queue an extra queryRow for prior-id lookups.

Made-with: Cursor
@owens1127
owens1127 merged commit 767755e into main Apr 24, 2026
7 checks passed
@owens1127
owens1127 deleted the webhook-subscriptions branch April 24, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants