Skip to content

Conversation

@brendan-kellam
Copy link
Contributor

@brendan-kellam brendan-kellam commented Nov 11, 2025

The following adds force re-sync buttons for repo indexing and connection syncing. There are located in the repo details page and connections detail page, respectively.

image image

This is accomplished by adding a internal api endpoint in the worker that exposes two endpoints, /api/sync-connection and /api/index-repo. Server actions proxy the requests to the worker endpoint.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added "Trigger sync" buttons for connections and repositories, enabling organization owners to manually trigger synchronization and indexing operations on demand.
    • Integrated loading indicators and success/error notifications to provide real-time feedback during sync operations.
  • Documentation

    • Added documentation for internal worker API utilities.

@github-actions

This comment has been minimized.

@coderabbitai
Copy link

coderabbitai bot commented Nov 11, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Introduces force resync buttons for connections and repositories via a new backend API server class. The server exposes POST endpoints /api/sync-connection and /api/index-repo that validate input, create indexing jobs through managers, and return job IDs. Frontend components conditionally display sync/index buttons for authorized users with corresponding request handlers and user feedback.

Changes

Cohort / File(s) Summary
API Server Implementation
packages/backend/src/api.ts
New Api class wiring Express app with /metrics and POST endpoints (/api/sync-connection, /api/index-repo); validates input via zod, creates jobs via ConnectionManager/RepoIndexManager, returns jobId; handles errors with 400/404 responses; listens on port 3060
Backend Initialization & Integration
packages/backend/src/index.ts
Added express-async-errors import; created Api instance in startup; wired api.dispose() into cleanup path; removed promClient.dispose() from shutdown
HTTP Server Separation
packages/backend/src/promClient.ts
Removed HTTP metrics server setup (Express app, /metrics route, PORT constant); made registry public; removed dispose method and server lifecycle management
Job Creation API
packages/backend/src/repoIndexManager.ts
Made createJobs method public; added return type Promise<string[]> returning job IDs
Dependencies
packages/backend/package.json
Added express-async-errors ^3.1.1 to devDependencies
Worker API Actions
packages/web/src/features/workerApi/actions.ts
New module exporting syncConnection(connectionId) and indexRepo(repoId); enforces OWNER role; POSTs to http://localhost:3060, validates response with zod, returns jobId
Worker API Documentation
packages/web/src/features/workerApi/README.md
Added README describing utilities to interact with internal worker REST API
Authorization Type Annotation
packages/web/src/withAuthV2.ts
Added explicit return type Promise<T | ServiceError> to withMinimumOrgRole
Repository Index UI
packages/web/src/app/[domain]/repos/[id]/page.tsx
Added role-based retrieval via getCurrentUserRole; conditionally show index button based on OWNER status; inject repoId and isIndexButtonVisible into RepoJobsTable
Repository Jobs Table
packages/web/src/app/[domain]/repos/components/repoJobsTable.tsx
Added repoId and isIndexButtonVisible props; introduced isIndexSubmitting state and onIndexButtonClick handler; replaced single Refresh button with button group including conditional "Trigger sync" LoadingButton; added indexRepo, isServiceError, LoadingButton imports
Connection Sync UI
packages/web/src/app/[domain]/settings/connections/[id]/page.tsx
Added id validation with notFound on NaN; inject connectionId into ConnectionJobsTable; import getConfigSettings
Connection Jobs Table
packages/web/src/app/[domain]/settings/connections/components/connectionJobsTable.tsx
Added connectionId prop; introduced isSyncSubmitting state and onSyncButtonClick handler; replaced single Refresh button with button group including conditional "Trigger sync" LoadingButton; added syncConnection, isServiceError, LoadingButton imports
Documentation
CHANGELOG.md
Added entry documenting force resync buttons for connections and repositories

Sequence Diagram

sequenceDiagram
    actor User
    participant Frontend as Frontend UI
    participant ServerAction as Server Action<br/>(withAuthV2)
    participant WorkerAPI as Worker API<br/>(Port 3060)
    participant Manager as Job Manager
    participant DB as Database

    User->>Frontend: Click "Trigger sync"<br/>button
    Frontend->>Frontend: Set isSubmitting=true
    Frontend->>ServerAction: Call syncConnection(id)<br/>or indexRepo(id)
    
    rect rgb(200, 220, 255)
    Note over ServerAction: Authorization Check
    ServerAction->>ServerAction: Verify OWNER role
    alt Role valid
        ServerAction->>WorkerAPI: POST /api/sync-connection<br/>or /api/index-repo
    else Role invalid
        ServerAction-->>Frontend: ServiceError
    end
    end
    
    rect rgb(200, 235, 200)
    Note over WorkerAPI: Validation & Job Creation
    WorkerAPI->>WorkerAPI: Validate input (zod)
    WorkerAPI->>DB: Fetch connection/repo
    alt Resource found
        WorkerAPI->>Manager: createJobs()
        Manager->>DB: Enqueue job
        Manager-->>WorkerAPI: Return jobId[]
        WorkerAPI-->>ServerAction: {jobId}
    else Not found
        WorkerAPI-->>ServerAction: 404 error
    end
    end
    
    ServerAction->>Frontend: Return jobId
    Frontend->>Frontend: Set isSubmitting=false
    Frontend->>Frontend: Show success toast
    Frontend->>Frontend: router.refresh()
    Frontend->>User: Update UI with<br/>new job status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas requiring attention:
    • Api class endpoint logic and error handling paths (validation, resource lookups, job creation)
    • Role-based authorization integration across frontend server actions
    • State management and async handler patterns in repoJobsTable and connectionJobsTable components (duplication of sync/index patterns)
    • HTTP client calls to localhost:3060 and response validation with zod schemas

Possibly related PRs

Suggested labels

sourcebot-team

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding force resync buttons for repositories and connections, which aligns with the primary functionality introduced across the changeset.

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.

@brendan-kellam
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 11, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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: 5

🧹 Nitpick comments (4)
packages/web/src/app/[domain]/repos/components/repoJobsTable.tsx (1)

197-215: Consider extracting jobId from success toast message.

The success toast displays the jobId, but this information is included inline in the message string. If you need to make the jobId clickable or copyable in the future, consider storing it separately.

Optional improvement:

         if (!isServiceError(response)) {
             const { jobId } = response;
             toast({
-                description: `✅ Repository sync triggered successfully. Job ID: ${jobId}`,
+                description: (
+                    <div className="flex items-center gap-2">
+                        <span>✅ Repository sync triggered successfully. Job ID:</span>
+                        <code className="text-xs">{jobId}</code>
+                    </div>
+                ),
             })
packages/web/src/features/workerApi/README.md (1)

1-1: Consider expanding the documentation.

While the current description is accurate, adding examples of the available endpoints (/api/sync-connection, /api/index-repo) and their request/response formats would improve developer experience.

Example enhancement:

# Worker API

This folder contains utilities to interact with the internal worker REST API. See packages/backend/api.ts

## Available Endpoints

- `POST /api/sync-connection` - Trigger a connection sync
- `POST /api/index-repo` - Trigger a repository index

## Usage

```typescript
import { syncConnection, indexRepo } from './actions';

// Sync a connection
const result = await syncConnection(connectionId);

// Index a repository
const result = await indexRepo(repoId);

</blockquote></details>
<details>
<summary>packages/web/src/features/workerApi/actions.ts (1)</summary><blockquote>

`11-59`: **Consider extracting common fetch logic.**

Both actions share nearly identical structure (fetch → check response → parse → validate). A helper function could reduce duplication:



```typescript
const callWorkerApi = async <T extends { jobId: string }>(
    endpoint: string,
    body: Record<string, unknown>,
    errorMessage: string
): Promise<T | ServiceError> => {
    const response = await fetch(`${WORKER_API_URL}${endpoint}`, {
        method: 'POST',
        body: JSON.stringify(body),
        headers: { 'Content-Type': 'application/json' },
    });

    if (!response.ok) {
        return unexpectedError(errorMessage);
    }

    const data = await response.json();
    const schema = z.object({ jobId: z.string() });
    return schema.parse(data) as T;
};

export const syncConnection = async (connectionId: number) => sew(() =>
    withAuthV2(({ role }) =>
        withMinimumOrgRole(role, OrgRole.OWNER, () =>
            callWorkerApi('/api/sync-connection', { connectionId }, 'Failed to sync connection')
        )
    )
);
packages/web/src/app/[domain]/settings/connections/components/connectionJobsTable.tsx (1)

195-212: Consider using finally block for loading state cleanup.

The loading state reset should be in a finally block to ensure it's always executed, even if an unexpected error occurs:

 const onSyncButtonClick = React.useCallback(async () => {
     setIsSyncSubmitting(true);
-    const response = await syncConnection(connectionId);
-
-    if (!isServiceError(response)) {
-        const { jobId } = response;
-        toast({
-            description: `✅ Connection synced successfully. Job ID: ${jobId}`,
-        })
-        router.refresh();
-    } else {
-        toast({
-            description: `❌ Failed to sync connection. ${response.message}`,
-        });
+    try {
+        const response = await syncConnection(connectionId);
+
+        if (!isServiceError(response)) {
+            const { jobId } = response;
+            toast({
+                description: `✅ Connection synced successfully. Job ID: ${jobId}`,
+            })
+            router.refresh();
+        } else {
+            toast({
+                description: `❌ Failed to sync connection. ${response.message}`,
+            });
+        }
+    } finally {
+        setIsSyncSubmitting(false);
     }
-
-    setIsSyncSubmitting(false);
 }, [connectionId, router, toast]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dfafda and 5d6f0bf.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • CHANGELOG.md (1 hunks)
  • packages/backend/package.json (1 hunks)
  • packages/backend/src/api.ts (1 hunks)
  • packages/backend/src/index.ts (3 hunks)
  • packages/backend/src/promClient.ts (1 hunks)
  • packages/backend/src/repoIndexManager.ts (2 hunks)
  • packages/web/src/app/[domain]/repos/[id]/page.tsx (4 hunks)
  • packages/web/src/app/[domain]/repos/components/repoJobsTable.tsx (5 hunks)
  • packages/web/src/app/[domain]/settings/connections/[id]/page.tsx (3 hunks)
  • packages/web/src/app/[domain]/settings/connections/components/connectionJobsTable.tsx (3 hunks)
  • packages/web/src/features/workerApi/README.md (1 hunks)
  • packages/web/src/features/workerApi/actions.ts (1 hunks)
  • packages/web/src/withAuthV2.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

Filenames should always be camelCase. Exception: if there are filenames in the same directory with a format other than camelCase, use that format to keep things consistent.

Files:

  • packages/web/src/features/workerApi/README.md
  • CHANGELOG.md
  • packages/web/src/app/[domain]/repos/[id]/page.tsx
  • packages/web/src/app/[domain]/repos/components/repoJobsTable.tsx
  • packages/backend/src/index.ts
  • packages/web/src/withAuthV2.ts
  • packages/backend/src/repoIndexManager.ts
  • packages/backend/src/api.ts
  • packages/web/src/features/workerApi/actions.ts
  • packages/backend/package.json
  • packages/backend/src/promClient.ts
  • packages/web/src/app/[domain]/settings/connections/components/connectionJobsTable.tsx
  • packages/web/src/app/[domain]/settings/connections/[id]/page.tsx
🔇 Additional comments (17)
CHANGELOG.md (1)

14-16: LGTM!

The changelog entry correctly documents the new force resync feature and follows the established format.

packages/web/src/app/[domain]/repos/components/repoJobsTable.tsx (2)

182-190: LGTM!

The component signature correctly extends the props with repoId and isIndexButtonVisible for conditional button rendering.


281-305: LGTM!

The button group layout and conditional rendering logic are well-structured. The LoadingButton properly shows loading state during async operations.

packages/web/src/app/[domain]/repos/[id]/page.tsx (2)

55-58: LGTM!

The user role retrieval and error handling are correctly implemented. This enables role-based access control for the index button visibility.


181-186: LGTM!

The role-based visibility check (userRole === OrgRole.OWNER) correctly restricts the index button to owners only. This aligns with security best practices for sensitive operations.

packages/backend/src/index.ts (3)

5-5: LGTM!

The import of express-async-errors for side effects is correct. This package automatically wraps Express async handlers to catch rejected promises.


77-82: LGTM!

The Api instance is correctly instantiated with all required dependencies (promClient, prisma, connectionManager, repoIndexManager).


111-111: LGTM!

The Api instance is properly disposed during cleanup, ensuring graceful shutdown of the HTTP server and any associated resources.

packages/backend/src/repoIndexManager.ts (1)

195-226: LGTM!

The method visibility change to public and the addition of the return value (jobs.map(job => job.id)) correctly enable external callers to trigger indexing jobs and receive the created job IDs. This is necessary for the new API endpoints to return job IDs to clients.

packages/web/src/app/[domain]/settings/connections/[id]/page.tsx (2)

28-31: LGTM!

The input validation correctly handles invalid connection IDs by returning notFound() when the parsed ID is NaN. This prevents runtime errors from invalid route parameters.


178-181: LGTM!

The connectionId prop is correctly passed to ConnectionJobsTable, enabling the sync functionality. This change aligns with the corresponding updates to the table component.

packages/web/src/withAuthV2.ts (1)

180-184: LGTM! Explicit return type improves type safety.

The addition of an explicit return type Promise<T | ServiceError> enhances type safety and makes the function signature clearer. This aligns well with the worker API actions that rely on this typing.

packages/web/src/app/[domain]/settings/connections/components/connectionJobsTable.tsx (1)

264-286: LGTM! Well-structured UI controls.

The button group provides clear actions with appropriate visual feedback. The use of LoadingButton for the sync action ensures good UX during async operations.

packages/backend/src/promClient.ts (1)

3-3: LGTM! Good separation of concerns.

Making registry public enables the Api class to expose metrics via HTTP while keeping metric collection logic in PromClient. This refactor properly separates HTTP server concerns from metrics collection.

packages/backend/src/api.ts (3)

91-92: Verify array destructuring safety.

Same concern as in syncConnection: the destructuring assumes at least one job ID is returned. Verify that RepoIndexManager.createJobs always returns at least one element, or add validation as suggested above.


95-102: LGTM! Proper graceful shutdown.

The dispose method correctly wraps server.close in a Promise for proper async cleanup. This ensures graceful shutdown of the HTTP server.


65-67: No changes required to array destructuring—current usage is safe.

The ConnectionManager.createJobs method (at line 120) returns jobs.map(job => job.id). When called with a single-element array [connection], it reliably creates exactly one job and returns a single-element array. Since the connection is validated at line 60 before being passed to createJobs, the destructuring const [jobId] is safe.

The defensive check suggested in the review would only be necessary if empty arrays were possible inputs in this code path, which they are not.

@brendan-kellam brendan-kellam merged commit 18fad64 into main Nov 11, 2025
9 checks passed
@brendan-kellam brendan-kellam deleted the bkellam/force_resync branch November 11, 2025 23:16
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