Skip to content

[FEATURE] Add campaign updates / creator posts endpoint (#566) - #727

Open
TheDEV111 wants to merge 2 commits into
ritik4ever:mainfrom
TheDEV111:feature/566-campaign-updates
Open

[FEATURE] Add campaign updates / creator posts endpoint (#566)#727
TheDEV111 wants to merge 2 commits into
ritik4ever:mainfrom
TheDEV111:feature/566-campaign-updates

Conversation

@TheDEV111

@TheDEV111 TheDEV111 commented Jul 29, 2026

Copy link
Copy Markdown

Description

Resolves #566 by adding endpoints for campaign creators to post text updates/announcements to backers and for clients to fetch campaign updates.

Features & Implementation Details

  • Schema Migration (backend/src/services/db.ts):

    • Created campaign_updates table (id, campaign_id, creator_address, content, created_at) with performance indexes on campaign_id and created_at.
  • Notification Service (backend/src/services/notificationService.ts):

    • Implemented notifyContributorsOnUpdate to gather all unique non-refunded campaign backer addresses.
    • Added support for webhook notifications (WEBHOOK_URL environment variable) and event listeners.
  • Validation & Business Logic (backend/src/validation/schemas.ts, backend/src/services/campaignStore.ts):

    • Created Zod validation schema enforcing max 2,000 characters content length and valid Stellar account ID for creator address (creator, creatorAddress, or creator_address).
    • Added authorization check (403 FORBIDDEN if non-creator attempts to post).
    • Recorded update_posted event in audit history campaign_events.
  • REST API Endpoints (backend/src/index.ts):

    • POST /api/campaigns/:id/updates — Creator posts update text to backers (201 Created).
    • GET /api/campaigns/:id/updates — Retrieves campaign updates in reverse chronological order (200 OK).

Acceptance Criteria Verification

  • Only campaign creator can post updates (403 FORBIDDEN for non-creators).
  • Update content max 2,000 characters (400 VALIDATION_ERROR for content > 2,000 chars).
  • Contributors notified via configured channels (webhook delivery & event handlers).

Testing

  • Unit tests added in backend/src/services/__tests__/campaignUpdates.test.ts.
  • Integration tests added in backend/src/__tests__/updatesApi.test.ts.
  • All 33 test suites passing cleanly.

Summary by CodeRabbit

  • New Features

    • Added campaign updates, allowing creators to post and view updates.
    • Added validation for update content, including a 2,000-character limit.
    • Added notifications for active campaign contributors when new updates are posted.
    • Recorded update activity in campaign event history.
  • Tests

    • Added coverage for creator authorization, validation, ordering, notifications, missing campaigns, and API responses.

@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

@TheDEV111 is attempting to deploy a commit to the ritik4ever's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Campaign updates now support creator-only posting and retrieval through HTTP endpoints. Updates are stored in SQLite, recorded in campaign events, and sent to active contributors through registered handlers or an optional webhook.

Changes

Campaign Updates

Layer / File(s) Summary
Update contracts and persistence
backend/src/services/db.ts, backend/src/services/campaignStore.ts, backend/src/validation/schemas.ts
Adds the campaign_updates table, indexes, request validation, and normalized update records.
Contributor notification delivery
backend/src/services/notificationService.ts, backend/src/services/campaignStore.ts
Looks up active contributors, dispatches handlers, logs delivery events, and optionally posts to a webhook.
Update service operations
backend/src/services/campaignStore.ts, backend/src/services/__tests__/campaignUpdates.test.ts
Adds creator authorization, update creation, event recording, notification triggering, reverse-chronological retrieval, and service coverage.
HTTP endpoint integration
backend/src/index.ts, backend/src/index.test.ts, backend/src/__tests__/updatesApi.test.ts
Adds POST and GET campaign update routes, initializes the store in tests, and covers endpoint responses and validation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ExpressApp
  participant campaignStore
  participant SQLite
  participant notificationService
  Client->>ExpressApp: POST /api/campaigns/:id/updates
  ExpressApp->>ExpressApp: Validate campaign ID and payload
  ExpressApp->>campaignStore: createCampaignUpdate(campaignId, input)
  campaignStore->>SQLite: Insert update and record update_posted event
  campaignStore->>notificationService: notifyContributorsOnUpdate(update)
  notificationService->>SQLite: Query non-refunded contributors
  notificationService-->>campaignStore: NotificationPayload
  campaignStore-->>ExpressApp: CampaignUpdateRecord
  ExpressApp-->>Client: 201 update response
Loading

Possibly related PRs

Suggested reviewers: queenfrostbite

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The migration removes the webhook dead-letter queue and campaign-comments indexes, which are unrelated to issue #566. Restore the unrelated migration removals or submit them in a separate pull request with appropriate requirements.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the campaign updates and creator posts endpoint feature.
Linked Issues check ✅ Passed The changes implement both endpoints, creator authorization, 2,000-character validation, persistence, and contributor notifications required by issue #566.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.5)
backend/src/index.ts

File contains syntax errors that prevent linting: Line 514: await is only allowed within async functions and at the top levels of modules.; Line 529: await is only allowed within async functions and at the top levels of modules.; Line 535: expected , but instead found catch; Line 535: expected , but instead found (; Line 535: expected , but instead found {; Line 536: Expected a function body but instead found ';'.; Line 537: Expected a statement but instead found '}
})'.

backend/src/validation/schemas.ts

File contains syntax errors that prevent linting: Line 225: Illegal use of an export declaration not at the top level; Line 246: Illegal use of an export declaration not at the top level; Line 251: Illegal use of an export declaration not at the top level; Line 258: Illegal use of an export declaration not at the top level; Line 264: Illegal use of an export declaration not at the top level; Line 273: Illegal use of an export declaration not at the top level; Line 275: Illegal use of an export declaration not at the top level; Line 280: Illegal use of an export declaration not at the top level; Line 289: Illegal use of an export declaration not at the top level; Line 293: Illegal use of an export declaration not at the top level; Line 312: expected } but instead the file ends

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

backend/src/index.ts

Parsing error: ',' expected.

backend/src/validation/schemas.ts

Parsing error: '}' expected.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
backend/src/services/campaignStore.ts (1)

716-731: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

No pagination on getCampaignUpdates.

This returns every update for a campaign on each call. For long-running or highly active campaigns this result set only grows; consider adding limit/offset (or cursor) parameters.

🤖 Prompt for 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.

In `@backend/src/services/campaignStore.ts` around lines 716 - 731, Add pagination
to getCampaignUpdates by accepting limit and offset (or the project’s
established cursor parameters), applying them to the campaign_updates query, and
returning only the requested page while preserving the existing campaign
validation and ordering.
backend/src/services/db.ts (1)

108-121: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider a composite index instead of two single-column indexes.

getCampaignUpdates filters by campaign_id and orders by created_at DESC, id DESC. A composite index (campaign_id, created_at DESC, id DESC) lets SQLite satisfy both the filter and the sort in one index scan; the two separate single-column indexes here can only be used for the filter, with the sort still done separately.

♻️ Suggested composite index
-    CREATE INDEX IF NOT EXISTS idx_campaign_updates_campaign_id ON campaign_updates(campaign_id);
-    CREATE INDEX IF NOT EXISTS idx_campaign_updates_created_at ON campaign_updates(created_at);
+    CREATE INDEX IF NOT EXISTS idx_campaign_updates_campaign_created ON campaign_updates(campaign_id, created_at DESC, id DESC);
🤖 Prompt for 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.

In `@backend/src/services/db.ts` around lines 108 - 121, Replace the separate
campaign_updates indexes on campaign_id and created_at with one composite index
covering (campaign_id, created_at DESC, id DESC), matching getCampaignUpdates
filtering and ordering while preserving the existing index naming convention.
🤖 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 `@backend/src/services/campaignStore.ts`:
- Around line 659-670: Replace the client-supplied creator address comparison in
the campaign update authorization flow with the app’s existing authenticated
Stellar identity or transaction-verification mechanism, if available. If no such
mechanism exists, require and validate a signed challenge proving control of
campaign.creator before allowing updates. Keep the 403 response for callers who
cannot prove ownership, and do not treat input.creatorAddress, creator, or
creator_address as authorization by itself.
- Around line 697-703: Handle the asynchronous notifyContributorsOnUpdate call
so failures cannot become unhandled promise rejections: either await it within
appropriate error handling or attach a rejection handler that logs the failure
while preserving the successful update response. Keep the update write behavior
unchanged and modify the call site in the update flow.

In `@backend/src/services/notificationService.ts`:
- Around line 72-77: Update the campaign_update_notification_sent log in
notificationService to remove the recipients field and retain only
recipientCount and the other non-sensitive campaign/update metadata.

---

Nitpick comments:
In `@backend/src/services/campaignStore.ts`:
- Around line 716-731: Add pagination to getCampaignUpdates by accepting limit
and offset (or the project’s established cursor parameters), applying them to
the campaign_updates query, and returning only the requested page while
preserving the existing campaign validation and ordering.

In `@backend/src/services/db.ts`:
- Around line 108-121: Replace the separate campaign_updates indexes on
campaign_id and created_at with one composite index covering (campaign_id,
created_at DESC, id DESC), matching getCampaignUpdates filtering and ordering
while preserving the existing index naming convention.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dad17c6a-b1d1-43ba-b8bc-f01a5b48260e

📥 Commits

Reviewing files that changed from the base of the PR and between 10f827c and 305ae33.

⛔ Files ignored due to path filters (1)
  • backend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • backend/src/__tests__/updatesApi.test.ts
  • backend/src/index.test.ts
  • backend/src/index.ts
  • backend/src/services/__tests__/campaignUpdates.test.ts
  • backend/src/services/campaignStore.ts
  • backend/src/services/db.ts
  • backend/src/services/notificationService.ts
  • backend/src/validation/schemas.ts

Comment thread backend/src/services/campaignStore.ts
Comment thread backend/src/services/campaignStore.ts
Comment thread backend/src/services/notificationService.ts
@ritik4ever

Copy link
Copy Markdown
Owner

Hi @TheDEV111,

This PR could not be merged because it has merge conflicts with the target branch.

Please resolve the merge conflicts, push the updated changes, and the PR can be reviewed and merged.

Thank you!

1 similar comment
@ritik4ever

Copy link
Copy Markdown
Owner

Hi @TheDEV111,

This PR could not be merged because it has merge conflicts with the target branch.

Please resolve the merge conflicts, push the updated changes, and the PR can be reviewed and merged.

Thank you!

@drips-wave

drips-wave Bot commented Aug 3, 2026

Copy link
Copy Markdown

@TheDEV111 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@TheDEV111

Copy link
Copy Markdown
Author

@ritik4ever conflicts resolved, apologies for the late response kindly review and merge

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
backend/src/services/db.ts (1)

139-152: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Restore the deleted webhook_dead_letter_queue table or remove its consumers.

webhookService.ts still reads from, inserts into, deletes, and retries rows from webhook_dead_letter_queue, so removing its schema object will break webhook fallback handling. Keep the table creation in the schema or remove the queue code path before merge.

🤖 Prompt for 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.

In `@backend/src/services/db.ts` around lines 139 - 152, Restore the
webhook_dead_letter_queue table definition in the database schema used by the
initialization flow, ensuring it matches the columns and constraints consumed by
webhookService.ts for reading, inserting, deleting, and retrying queue entries;
otherwise remove all corresponding queue consumers, but preserve webhook
fallback handling.
backend/src/services/campaignStore.ts (1)

522-533: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Restore the missing dispatchWebhook and createNotification imports.

backend/src/services/campaignStore.ts now imports only notifyContributorsOnUpdate, but still calls dispatchWebhook multiple times and createNotification at lines 1052, 1071, and 1361. This will cause module import/compilation failure. Re-add the required imports or refactor those call sites.

🤖 Prompt for 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.

In `@backend/src/services/campaignStore.ts` around lines 522 - 533, Update the
imports in campaignStore.ts to include the existing dispatchWebhook and
createNotification symbols alongside notifyContributorsOnUpdate, preserving all
current call sites and avoiding any unrelated refactoring.
backend/src/index.test.ts (1)

270-338: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bind parseCampaignListQuery in the test file.

backend/src/index.ts imports and calls parseCampaignListQuery, but backend/src/index.test.ts only binds parseCampaignListFilters; therefore every test in this block has no lexical parseCampaignListQuery binding and throws a ReferenceError.

🤖 Prompt for 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.

In `@backend/src/index.test.ts` around lines 270 - 338, Update the imports or
setup in the test file so parseCampaignListQuery is lexically bound alongside
parseCampaignListFilters before the “Query parameter validation” tests use it.
Preserve the existing parser test cases and ensure each invocation resolves to
the intended implementation from backend/src/index.ts.
backend/src/index.ts (3)

89-89: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Split the merged import and statement on Line 89.

export const app = express(); is appended to the closing line of the ./services/campaignCache import. This is another merge artifact. Move the statement to its own line.

♻️ Proposed fix
-} from './services/campaignCache';export const app = express();
+} from './services/campaignCache';
+
+export const app = express();
🤖 Prompt for 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.

In `@backend/src/index.ts` at line 89, Separate the `export const app =
express();` declaration from the closing `./services/campaignCache` import line,
placing the export statement on its own line while preserving the import
unchanged.

507-538: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Fix the broken GET /api/campaigns/:id handler. The file does not parse.

The handler is declared as a synchronous callback with only (req, res), but the body uses await at Line 514 and Line 529 and closes with } catch (error) { next(error); } at Lines 535-537. There is no async keyword, no next parameter, and no opening try {. Biome reports parse errors on exactly these lines. This looks like an unresolved merge conflict. The backend cannot compile in this state.

🐛 Proposed fix
-app.get('/api/campaigns/:id', (req: Request, res: Response) => {
-  const parsedId = parseCampaignId(req.params.id);
-  if (!parsedId.ok) {
-    sendValidationError(parsedId.issues);
-  }
-
+app.get('/api/campaigns/:id', async (req: Request, res: Response, next: express.NextFunction) => {
+  try {
+    const parsedId = parseCampaignId(req.params.id);
+    if (!parsedId.ok) {
+      sendValidationError(parsedId.issues);
+    }
+
     const cacheKey = `campaigns:detail:${parsedId.value}`;
🤖 Prompt for 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.

In `@backend/src/index.ts` around lines 507 - 538, Fix the GET /api/campaigns/:id
handler by declaring its callback async, adding the next parameter, and wrapping
the existing handler logic in an opening try block to match the closing catch.
Preserve the current validation, cache, campaign lookup, response, and
error-forwarding behavior using the existing symbols parseCampaignId,
getCampaignCacheEntry, setCampaignCacheEntry, and next.

Source: Linters/SAST tools


634-638: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Import idempotencyMiddleware before using it.

backend/src/index.ts calls idempotencyMiddleware at line 637 but does not import it from ./middleware/idempotencyMiddleware. Add the import so npm run build succeeds.

🤖 Prompt for 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.

In `@backend/src/index.ts` around lines 634 - 638, Import idempotencyMiddleware
from ./middleware/idempotencyMiddleware in backend/src/index.ts before the
app.post route uses it, preserving the existing middleware order for the pledges
endpoint.
🧹 Nitpick comments (3)
backend/src/services/db.ts (1)

151-152: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add an index for the update retrieval query.

getCampaignUpdates filters by campaign_id and orders by created_at DESC, id DESC. The two single-column indexes cannot efficiently serve both requirements. Add one composite index to avoid sorting or scanning unrelated campaign updates.

Proposed fix
-    CREATE INDEX IF NOT EXISTS idx_campaign_updates_campaign_id ON campaign_updates(campaign_id);
-    CREATE INDEX IF NOT EXISTS idx_campaign_updates_created_at ON campaign_updates(created_at);
+    CREATE INDEX IF NOT EXISTS idx_campaign_updates_campaign_created_id
+    ON campaign_updates(campaign_id, created_at DESC, id DESC);
🤖 Prompt for 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.

In `@backend/src/services/db.ts` around lines 151 - 152, Update the schema/index
definitions near getCampaignUpdates to add a composite index on campaign_updates
covering campaign_id, created_at, and id in the query’s filtering and descending
ordering sequence. Keep the existing single-column indexes unchanged unless the
surrounding schema logic explicitly replaces redundant indexes.
backend/src/index.test.ts (1)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused ValidationModule type alias.

The bootstrap no longer imports the validation module dynamically. ESLint reports this alias as an error, which can fail lint in CI.

♻️ Proposed fix
 type DbModule = typeof import('./services/db');
-type ValidationModule = typeof import('./validation/schemas');
🤖 Prompt for 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.

In `@backend/src/index.test.ts` at line 13, Remove the unused ValidationModule
type alias from the test file, leaving the remaining bootstrap test setup
unchanged.

Source: Linters/SAST tools

backend/src/services/campaignStore.ts (1)

262-277: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

checkContributorLimit is unused and duplicates inline logic.

ESLint reports this function as unused. addPledge (Lines 824-832) and reconcileOnChainPledge (Lines 969-977) repeat the same check inline. Either call this helper from both transactions or delete it.

🤖 Prompt for 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.

In `@backend/src/services/campaignStore.ts` around lines 262 - 277, Remove the
unused checkContributorLimit helper, or refactor addPledge and
reconcileOnChainPledge to call it instead of duplicating the contributor-limit
validation. Ensure both transaction paths retain the existing
maximum-per-contributor behavior and error handling without leaving duplicate
logic.

Source: Linters/SAST tools

🤖 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 `@backend/src/validation/schemas.ts`:
- Around line 230-234: Update the content field in createCampaignPayloadSchema
to apply the same script-tag and SQL-comment rejection checks and sanitizeInput
transform used by title and description, while preserving its existing trim and
length validation.

---

Outside diff comments:
In `@backend/src/index.test.ts`:
- Around line 270-338: Update the imports or setup in the test file so
parseCampaignListQuery is lexically bound alongside parseCampaignListFilters
before the “Query parameter validation” tests use it. Preserve the existing
parser test cases and ensure each invocation resolves to the intended
implementation from backend/src/index.ts.

In `@backend/src/index.ts`:
- Line 89: Separate the `export const app = express();` declaration from the
closing `./services/campaignCache` import line, placing the export statement on
its own line while preserving the import unchanged.
- Around line 507-538: Fix the GET /api/campaigns/:id handler by declaring its
callback async, adding the next parameter, and wrapping the existing handler
logic in an opening try block to match the closing catch. Preserve the current
validation, cache, campaign lookup, response, and error-forwarding behavior
using the existing symbols parseCampaignId, getCampaignCacheEntry,
setCampaignCacheEntry, and next.
- Around line 634-638: Import idempotencyMiddleware from
./middleware/idempotencyMiddleware in backend/src/index.ts before the app.post
route uses it, preserving the existing middleware order for the pledges
endpoint.

In `@backend/src/services/campaignStore.ts`:
- Around line 522-533: Update the imports in campaignStore.ts to include the
existing dispatchWebhook and createNotification symbols alongside
notifyContributorsOnUpdate, preserving all current call sites and avoiding any
unrelated refactoring.

In `@backend/src/services/db.ts`:
- Around line 139-152: Restore the webhook_dead_letter_queue table definition in
the database schema used by the initialization flow, ensuring it matches the
columns and constraints consumed by webhookService.ts for reading, inserting,
deleting, and retrying queue entries; otherwise remove all corresponding queue
consumers, but preserve webhook fallback handling.

---

Nitpick comments:
In `@backend/src/index.test.ts`:
- Line 13: Remove the unused ValidationModule type alias from the test file,
leaving the remaining bootstrap test setup unchanged.

In `@backend/src/services/campaignStore.ts`:
- Around line 262-277: Remove the unused checkContributorLimit helper, or
refactor addPledge and reconcileOnChainPledge to call it instead of duplicating
the contributor-limit validation. Ensure both transaction paths retain the
existing maximum-per-contributor behavior and error handling without leaving
duplicate logic.

In `@backend/src/services/db.ts`:
- Around line 151-152: Update the schema/index definitions near
getCampaignUpdates to add a composite index on campaign_updates covering
campaign_id, created_at, and id in the query’s filtering and descending ordering
sequence. Keep the existing single-column indexes unchanged unless the
surrounding schema logic explicitly replaces redundant indexes.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 282123ac-5886-4d7c-9131-57c0634ecab0

📥 Commits

Reviewing files that changed from the base of the PR and between 305ae33 and 96399ec.

📒 Files selected for processing (5)
  • backend/src/index.test.ts
  • backend/src/index.ts
  • backend/src/services/campaignStore.ts
  • backend/src/services/db.ts
  • backend/src/validation/schemas.ts

Comment on lines +230 to +234
content: z
.string()
.trim()
.min(1, "Update content cannot be empty.")
.max(2000, "Update content cannot exceed 2000 characters."),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Apply the same sanitization to content that other user text fields use.

createCampaignPayloadSchema rejects script tags and SQL comment sequences on title and description, then runs sanitizeInput. Update content is free-form text authored by a creator and rendered to backers. Without the same guards, stored XSS payloads pass validation.

🛡️ Proposed fix
     content: z
       .string()
       .trim()
       .min(1, "Update content cannot be empty.")
-      .max(2000, "Update content cannot exceed 2000 characters."),
+      .max(2000, "Update content cannot exceed 2000 characters.")
+      .refine((val) => !containsScriptTag(val), 'Content cannot contain script tags.')
+      .refine((val) => !containsSqlComment(val), 'Content cannot contain SQL comment sequences.')
+      .transform((val) => sanitizeInput(val)),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
content: z
.string()
.trim()
.min(1, "Update content cannot be empty.")
.max(2000, "Update content cannot exceed 2000 characters."),
content: z
.string()
.trim()
.min(1, "Update content cannot be empty.")
.max(2000, "Update content cannot exceed 2000 characters.")
.refine((val) => !containsScriptTag(val), 'Content cannot contain script tags.')
.refine((val) => !containsSqlComment(val), 'Content cannot contain SQL comment sequences.')
.transform((val) => sanitizeInput(val)),
🤖 Prompt for 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.

In `@backend/src/validation/schemas.ts` around lines 230 - 234, Update the content
field in createCampaignPayloadSchema to apply the same script-tag and
SQL-comment rejection checks and sanitizeInput transform used by title and
description, while preserving its existing trim and length validation.

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.

[FEATURE] Add campaign updates / creator posts endpoint

2 participants