Skip to content

feat: add user metadata support and webhook integration#3063

Open
Thiteago wants to merge 12 commits into
seerr-team:developfrom
Thiteago:user-metadata
Open

feat: add user metadata support and webhook integration#3063
Thiteago wants to merge 12 commits into
seerr-team:developfrom
Thiteago:user-metadata

Conversation

@Thiteago
Copy link
Copy Markdown

@Thiteago Thiteago commented May 22, 2026

Description

This PR introduces a custom metadata management system for users, allowing administrators to define custom key-value pairs per user. These fields are specifically useful for custom integrations, providing the ability to store extra information (such as internal IDs or environment-specific tags) that can be accessed via webhooks.

The changes that i made was:

  • Adjustment on user settings and seer settings to provide the new menu tab called metadata
  • Created an component to dispose a form with metadata information, and buttons to makes the CRUD possible.
  • Created two new endpoints, to get and create/update/destroy an user metadata.
  • Adjusted the webhook process to parse the new metadata keys.
  • Add metadata information on docs.
  • Created the metadata table and created the relation with the user.

How Has This Been Tested?

I tested using an external server as a webhook destiny, so i triggered all the events available and see if i receives the request on this external server. It works.
I also tested the conditions where the inputs are badly inputed (empty or with special chars), and it also worked.

Screenshots

image image image

AI Disclosure: I used Gemini Pro to better understand Seerr's architecture for my first contribution here. I also needed to learn how Seerr uses TypeORM (I'm more used to Prisma) and how the different scopes are managed, since the server API and the Next.js frontend are part of the same project.

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • New Features

    • Users can create/manage custom key/value metadata in account settings with a "Sensitive" toggle.
    • Admins can edit other users' metadata via a new settings page.
    • API endpoints added to read/update a user's metadata.
    • Webhooks can include user metadata via a {{metadata}} token.
  • Documentation

    • Webhook docs updated to describe the {{metadata}} template and when it's emitted.
  • UX

    • Form validation for required/unique keys and save success/error toasts.

Review Change Stack

@Thiteago Thiteago requested a review from a team as a code owner May 22, 2026 22:13
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds per-user metadata: new UserMetadata entity and migrations, API endpoints to get/replace metadata, webhook/template support for injecting {{metadata}}, and frontend pages/components with i18n to manage per-user key/value metadata including sensitivity flags.

Changes

User Metadata Storage and Management

Layer / File(s) Summary
Data model and database migrations
server/entity/User.ts, server/entity/UserMetadata.ts, server/migration/postgres/1779478559332-AddUserMetadata.ts, server/migration/sqlite/1779478551509-AddUserMetadata.ts
Adds UserMetadata entity (id, key, value, isSensitive) with uniqueness on (user,key), OneToMany from User, and migrations to add isSensitive and rebuild SQLite tables.
API contract and backend route handlers
seerr-api.yml, server/routes/user/usersettings.ts
Adds OpenAPI paths and Express routes: GET /user/{id}/settings/metadata returns { metadata: [...] }, POST /user/{id}/settings/metadata atomically replaces the user's metadata using a transaction and validates reserved/duplicate keys.
Webhook metadata template integration
server/lib/notifications/agents/webhook.ts, docs/using-seerr/notifications/webhook.md, src/components/Settings/Notifications/NotificationsWebhook/index.tsx
WebhookAgent now resolves a target user, loads their metadata relation, builds a safe key/value map, and injects it into webhook payloads via {{metadata}}; docs and default webhook template updated to document the variable.
Frontend metadata management UI and settings integration
src/components/UserProfile/UserSettings/UserMetadataSettings/index.tsx, src/components/UserProfile/UserSettings/index.tsx, src/pages/profile/settings/metadata.tsx, src/pages/users/[userId]/settings/metadata.tsx, src/i18n/locale/en.json
New UserMetadataSettings React component with Formik/Yup for rows of {key,value,isSensitive}, sensitive values use masked input, POSTs to metadata API, integrates metadata tab/pages and adds i18n strings.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SettingsUI
  participant MetadataAPI as /api/v1/user/{id}/settings/metadata
  participant UserRepo as UserRepository
  participant WebhookAgent
  participant ExternalWebhook
  
  User->>SettingsUI: Fill metadata form
  SettingsUI->>SettingsUI: Validate (Formik/Yup)
  SettingsUI->>MetadataAPI: POST {metadata: [{key, value, isSensitive}]}
  MetadataAPI->>UserRepo: Delete old metadata rows
  MetadataAPI->>UserRepo: Create new UserMetadata entities
  UserRepo-->>MetadataAPI: Saved metadata
  MetadataAPI-->>SettingsUI: Success {metadata: [...]}
  SettingsUI->>User: Show success toast
  
  Note over WebhookAgent: Later, when a notification fires
  WebhookAgent->>UserRepo: Fetch target user with metadata relation
  UserRepo-->>WebhookAgent: user + metadata[]
  WebhookAgent->>WebhookAgent: Build payload with {{metadata}} injected
  WebhookAgent->>ExternalWebhook: POST webhook with metadata
  ExternalWebhook-->>WebhookAgent: 200 OK
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • fallenbagel
  • gauthier-th
  • M0NsTeRRR

Poem

🐰 I dug a little hole in code,
Keys and values safe and snug,
Sensitive seeds tucked under logs,
Webhooks hum a gentle tug,
Hooray — metadata hops into place!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding user metadata support and webhook integration. It directly reflects the core features introduced across the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Thiteago Thiteago changed the title add user metadata support and webhook integration Feat: add user metadata support and webhook integration May 22, 2026
@Thiteago Thiteago changed the title Feat: add user metadata support and webhook integration feat: add user metadata support and webhook integration May 22, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@seerr-api.yml`:
- Around line 5063-5064: The OpenAPI tag value "Users" is capitalized and will
create a separate docs group; locate the tags: - Users entries (the literal
string "Users") and replace them with the existing lowercase tag "users" so all
endpoints use the same tag group; update both occurrences mentioned (the tags
array entries around the given diff and the other occurrence noted) to use
"users".

In `@server/entity/User.ts`:
- Around line 123-124: The User entity's new relation property metadata can be
leaked by User.filter(); update the User.filteredFields list to include
"metadata" so that filter()/any serialized filtered response excludes the
metadata relation; locate the User class and add "metadata" to the static
filteredFields array (or equivalent filteredFields definition) to prevent
leaking sensitive values.

In `@server/entity/UserMetadata.ts`:
- Around line 9-19: Add a DB-level unique constraint to prevent duplicate
metadata keys per user: modify the UserMetadata entity (class UserMetadata) to
enforce uniqueness on the combination of user (user / userId) and key (property
key) so the database rejects inserts with the same (userId, key); implement this
by adding the appropriate unique constraint/decorator at the entity level
(referencing UserMetadata, key, and user) and run migrations to apply the
constraint.

In `@server/lib/notifications/agents/webhook.ts`:
- Around line 181-184: The metadata mapping in userWithMeta.metadata assigns
user-provided keys directly into userMetadata which allows prototype pollution;
change userMetadata initialization to a truly dictionary object (use
Object.create(null)) and when iterating userWithMeta.metadata in the block that
currently sets userMetadata![metadata.key] = metadata.value validate and reject
dangerous keys (e.g., '__proto__', 'prototype', 'constructor') and non-string
keys before assignment to ensure only safe, explicit keys are copied.

In `@server/routes/user/usersettings.ts`:
- Around line 64-78: The delete-then-reinsert of user metadata must be made
atomic: wrap the delete and subsequent save calls in a database transaction so
they either both succeed or both roll back. Update the route handler to perform
metadataRepository.manager.transaction (or use dataSource.transaction) and move
the calls to metadataRepository.delete({ user: { id: user.id } }) and
metadataRepository.save(newMetadata) inside the transaction callback (using the
transactional entity manager or transactional repository), and construct
UserMetadata instances as before but persist them via the transaction's manager
to ensure delete+insert are committed or rolled back together.
- Around line 31-32: The metadata endpoints lack the ownership/admin guard, so
add the existing isOwnProfileOrAdmin() middleware to the '/metadata' routes:
update the route declarations that use userSettingsRoutes.get('/metadata', ...)
and the corresponding update route (userSettingsRoutes.post or .put for
'/metadata') to include isOwnProfileOrAdmin() in the middleware chain before the
async handler; ensure you import/ reference the same isOwnProfileOrAdmin
function used by other user settings routes so the metadata endpoints enforce
the same ownership/admin checks.

In `@src/components/UserProfile/UserSettings/UserMetadataSettings/index.tsx`:
- Around line 60-84: The metadata array allows duplicate or whitespace-differing
keys; update the validation and submit flow to trim and enforce uniqueness: add
a Yup-level test on MetadataSchema.metadata that maps each item's key via
key?.trim() and fails if any trimmed key is empty or duplicated, and in the form
submit handler normalize the payload by mapping metadata items to use trimmed
keys before converting to an object/map; apply the same change to the other
metadata schema/validation block referenced (lines ~112-120) so both validation
and submission always use normalized, unique keys.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ab2eec7-c213-402b-b9d5-97f0f612ff3d

📥 Commits

Reviewing files that changed from the base of the PR and between 32169d9 and 719d444.

📒 Files selected for processing (13)
  • docs/using-seerr/notifications/webhook.md
  • seerr-api.yml
  • server/entity/User.ts
  • server/entity/UserMetadata.ts
  • server/lib/notifications/agents/webhook.ts
  • server/migration/postgres/1779478559332-AddUserMetadata.ts
  • server/migration/sqlite/1779478551509-AddUserMetadata.ts
  • server/routes/user/usersettings.ts
  • src/components/Settings/Notifications/NotificationsWebhook/index.tsx
  • src/components/UserProfile/UserSettings/UserMetadataSettings/index.tsx
  • src/components/UserProfile/UserSettings/index.tsx
  • src/pages/profile/settings/metadata.tsx
  • src/pages/users/[userId]/settings/metadata.tsx

Comment thread seerr-api.yml Outdated
Comment thread server/entity/User.ts
Comment thread server/entity/UserMetadata.ts
Comment thread server/lib/notifications/agents/webhook.ts Outdated
Comment thread server/routes/user/usersettings.ts Outdated
Comment thread server/routes/user/usersettings.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/routes/user/usersettings.ts`:
- Around line 76-84: The incoming metadata mapping (incomingMetadata ->
newMetadata) can include duplicate meta.key values which causes the DB unique
constraint to throw; validate and reject duplicates before constructing
UserMetadata: detect duplicates in req.body.metadata (incomingMetadata) by key,
and if any duplicate keys are found return res.status(400).json({ error:
'Duplicate metadata keys' }) (or similar) instead of proceeding to create new
UserMetadata instances; update the route handling that uses
incomingMetadata/newMetadata (and UserMetadata) to perform this upfront check
and short-circuit with 400 when duplicates exist.

In `@src/i18n/locale/en.json`:
- Around line 1500-1509: Add the missing locale entry
components.UserProfile.UserSettings.UserMetadata.validationUniqueKeys to the
English i18n block used by UserMetadataSettings; update the en.json bundle by
inserting that key alongside the other UserMetadata entries (e.g., a short
validation message like "Keys must be unique") so
components.UserProfile.UserSettings.UserMetadata.validationUniqueKeys is defined
and used by the UI.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 923698b9-81be-4552-bb6c-ff4fc5a313e0

📥 Commits

Reviewing files that changed from the base of the PR and between 719d444 and 2adeab7.

📒 Files selected for processing (7)
  • seerr-api.yml
  • server/entity/User.ts
  • server/entity/UserMetadata.ts
  • server/lib/notifications/agents/webhook.ts
  • server/routes/user/usersettings.ts
  • src/components/UserProfile/UserSettings/UserMetadataSettings/index.tsx
  • src/i18n/locale/en.json

Comment thread server/routes/user/usersettings.ts Outdated
Comment thread src/i18n/locale/en.json
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/routes/user/usersettings.ts`:
- Around line 77-84: The duplicate-key check and HTTP response must not occur
inside dataSource.transaction because it causes side effects and can send
headers twice; move the duplicate validation for incomingMetadata (the keys =
incomingMetadata.map(...) and the Set size check) to run before calling
dataSource.transaction and return res.status(400).json(...) there, OR if you
prefer to keep validation inside the transaction, throw a custom error (e.g.,
new BadRequestError('Duplicate metadata keys')) instead of calling res.status...
so the transaction will rollback and the outer catch can send
res.status(400).json(...); also ensure transactionalEntityManager.delete(...) is
only executed after validation passes so deletes cannot happen on invalid input.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d80679aa-f641-4632-ab76-abf75d152597

📥 Commits

Reviewing files that changed from the base of the PR and between 2adeab7 and 8d641ec.

📒 Files selected for processing (2)
  • server/routes/user/usersettings.ts
  • src/i18n/locale/en.json

Comment thread server/routes/user/usersettings.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/routes/user/usersettings.ts`:
- Around line 70-77: The validation currently builds incomingMetadata and keys
but doesn't reject webhook-reserved keys; extend the check in the same block
(where incomingMetadata, keys are computed) to detect any metadata key equal to
"__proto__", "prototype", or "constructor" and return a 400 JSON error (e.g.
"Reserved metadata keys are not allowed") just like the duplicate-key branch;
ensure you validate against req.body.metadata/incomingMetadata before proceeding
so these reserved keys are rejected server-side the same way they are stripped
by the webhook agent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f2f12379-805b-4426-a2eb-3316bf3845d5

📥 Commits

Reviewing files that changed from the base of the PR and between 8d641ec and e5a1a98.

📒 Files selected for processing (1)
  • server/routes/user/usersettings.ts

Comment thread server/routes/user/usersettings.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/UserProfile/UserSettings/UserMetadataSettings/index.tsx`:
- Around line 79-85: The required-field validator for the key (the function
associated with 'key-required') currently treats whitespace-only strings as
valid; update the validator to trim both the current value and the sibling meta
value before testing requiredness (e.g., use (value || '').trim() and (metaValue
|| '').trim()) so that whitespace-only inputs fail the required check and cannot
bypass validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1a39d6ff-ff81-4354-9a1b-6d050b6b2c23

📥 Commits

Reviewing files that changed from the base of the PR and between e5a1a98 and 56caf29.

📒 Files selected for processing (3)
  • server/routes/user/usersettings.ts
  • src/components/UserProfile/UserSettings/UserMetadataSettings/index.tsx
  • src/i18n/locale/en.json
✅ Files skipped from review due to trivial changes (1)
  • src/i18n/locale/en.json

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.

1 participant