feat: add user metadata support and webhook integration#3063
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds per-user metadata: new UserMetadata entity and migrations, API endpoints to get/replace metadata, webhook/template support for injecting ChangesUser Metadata Storage and Management
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (13)
docs/using-seerr/notifications/webhook.mdseerr-api.ymlserver/entity/User.tsserver/entity/UserMetadata.tsserver/lib/notifications/agents/webhook.tsserver/migration/postgres/1779478559332-AddUserMetadata.tsserver/migration/sqlite/1779478551509-AddUserMetadata.tsserver/routes/user/usersettings.tssrc/components/Settings/Notifications/NotificationsWebhook/index.tsxsrc/components/UserProfile/UserSettings/UserMetadataSettings/index.tsxsrc/components/UserProfile/UserSettings/index.tsxsrc/pages/profile/settings/metadata.tsxsrc/pages/users/[userId]/settings/metadata.tsx
There was a problem hiding this comment.
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
📒 Files selected for processing (7)
seerr-api.ymlserver/entity/User.tsserver/entity/UserMetadata.tsserver/lib/notifications/agents/webhook.tsserver/routes/user/usersettings.tssrc/components/UserProfile/UserSettings/UserMetadataSettings/index.tsxsrc/i18n/locale/en.json
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
server/routes/user/usersettings.tssrc/i18n/locale/en.json
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
server/routes/user/usersettings.ts
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
server/routes/user/usersettings.tssrc/components/UserProfile/UserSettings/UserMetadataSettings/index.tsxsrc/i18n/locale/en.json
✅ Files skipped from review due to trivial changes (1)
- src/i18n/locale/en.json
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:
metadataHow 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
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:
pnpm buildpnpm i18n:extractSummary by CodeRabbit
New Features
Documentation
UX