Skip to content

agent: @U0AJM7X8FBR Admin + Docs + API - we want a new admin page to view analy#330

Open
sweetmantech wants to merge 1 commit intotestfrom
agent/-u0ajm7x8fbr-admin---docs---ap-1774265132075
Open

agent: @U0AJM7X8FBR Admin + Docs + API - we want a new admin page to view analy#330
sweetmantech wants to merge 1 commit intotestfrom
agent/-u0ajm7x8fbr-admin---docs---ap-1774265132075

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented Mar 23, 2026

Automated PR from coding agent.

Summary by CodeRabbit

New Features

  • Added new admin API endpoint to retrieve Slack mentions directed at the coding agent bot, with optional time-period filtering by interval (all, daily, weekly, or monthly)
  • Endpoint returns comprehensive metadata for each mention, including user profiles, channel information, message content, timestamps, and user avatar URLs for administrative workflows and integrations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-api Ready Ready Preview Mar 23, 2026 11:28am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

A new admin API endpoint (GET /api/admins/coding-agent/slack-tags) is introduced to retrieve Slack mentions containing the bot. It includes query validation for filtering by period, integration with the Slack Web API to paginate channels and message history, and comprehensive error handling with CORS support.

Changes

Cohort / File(s) Summary
API Route Handler
app/api/admins/coding-agent/slack-tags/route.ts
Exports GET handler delegating to getSlackTagsHandler() and OPTIONS handler for CORS preflight with 204 response.
Validation & Query Processing
lib/admins/coding-agent/validateGetSlackTagsQuery.ts
Defines Zod schemas for query period validation (all/daily/weekly/monthly) with admin auth check; exports validateGetSlackTagsQuery() to parse and validate request query parameters.
Request Handler
lib/admins/coding-agent/getSlackTagsHandler.ts
Orchestrates query validation, invokes fetchSlackMentions(), and returns paginated Slack mention data with status metadata and CORS headers; includes error handling with 500 fallback.
Slack API Integration
lib/admins/coding-agent/fetchSlackMentions.ts
Implements Slack Web API client (slackGet()) to authenticate via bot token, paginate channels and message history with optional epoch cutoff, filter human messages containing bot mention, resolve user details (name/avatar), and return typed SlackTag results sorted newest-first.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Route as Route Handler
    participant Validator as Validator
    participant Handler as GET Handler
    participant Slack as Slack Web API
    participant Auth as Auth System

    Client->>Route: GET /api/admins/coding-agent/slack-tags?period=weekly
    Route->>Handler: getSlackTagsHandler(request)
    
    Handler->>Validator: validateGetSlackTagsQuery(request)
    Validator->>Auth: validateAdminAuth(request)
    
    alt Auth fails
        Auth-->>Validator: NextResponse (error)
        Validator-->>Handler: NextResponse (error)
        Handler-->>Route: NextResponse (error)
        Route-->>Client: 401/403 Response
    else Auth succeeds
        Auth-->>Validator: ✓
        Validator->>Validator: Extract & validate period
        Validator-->>Handler: { period: "weekly" }
        
        Handler->>Slack: auth.test()
        Slack-->>Handler: Bot user_id
        
        Handler->>Slack: conversations.list() + pagination
        Slack-->>Handler: Channel list
        
        loop For each channel
            Handler->>Slack: conversations.history(channel, oldest cutoff)
            Slack-->>Handler: Messages (paginated)
            
            loop For matching messages
                Handler->>Slack: users.info(user_id)
                Slack-->>Handler: User details (cached)
                Handler->>Handler: Build SlackTag
            end
        end
        
        Handler->>Handler: Sort by timestamp (newest-first)
        Handler-->>Route: NextResponse { status, total, tags, CORS headers }
        Route-->>Client: 200 JSON Response
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

The changes introduce new functionality across four coordinated files totaling 281 lines. While individual files are straightforward (route handler, validators, orchestrator), the fetchSlackMentions.ts module contains substantial logic spanning Slack API pagination, user caching, timestamp normalization, and filtering logic that warrants careful review. The changes follow consistent patterns (CORS headers, error handling) but demand separate reasoning for the Slack API integration complexity.

Poem

🤖✨ A new admin portal opens wide,
Slack mentions surfaced, nowhere to hide,
With pagination's dance and tokens in hand,
The bot finds its mentions across the land! 🎯

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning fetchSlackMentions.ts violates Single Responsibility Principle with ~180 lines combining 10+ distinct responsibilities including token validation, API auth, pagination loops, filtering, user caching, and data transformation. Extract helper functions for each major responsibility: getSlackBotUserId(), getAllChannels(), getChannelHistory(), resolveUserDetails(), transformToSlackTag(), keeping each under 20 lines.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/-u0ajm7x8fbr-admin---docs---ap-1774265132075

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.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/admins/coding-agent/slack-tags/route.ts`:
- Around line 17-18: Add a JSDoc block for the exported route handler OPTIONS
explaining its purpose (responds to preflight CORS requests), mention it returns
a 204 No Content with CORS headers, and annotate the return type
(Promise<NextResponse>); place the comment immediately above the export async
function OPTIONS() declaration and reference getCorsHeaders() in the description
to clarify where headers come from.
- Around line 13-18: The PR lacks automated tests for the new Slack tags
endpoint in route.ts (GET -> getSlackTagsHandler and OPTIONS preflight); add a
small test matrix that covers: successful GET (mock auth to return a valid user
and mock the Slack upstream to return tags, assert JSON and status 200), invalid
period parameter (call GET with bad period, assert 400), auth rejection (mock
auth to fail, assert 401/403 as implemented), Slack upstream failure (mock the
upstream call used by getSlackTagsHandler to return an error/timeout and assert
the handler returns the expected error/status), and the OPTIONS preflight (call
OPTIONS and assert status 204 and CORS headers via getCorsHeaders()). Use unit
tests on getSlackTagsHandler for orchestration logic and a lightweight
route-level test for OPTIONS; mock external deps (auth, Slack client,
getCorsHeaders) and keep tests minimal and focused.

In `@lib/admins/coding-agent/fetchSlackMentions.ts`:
- Around line 98-103: The code currently treats Slack API failures in
conversations.list and conversations.history by breaking out of loops, allowing
partial results to be returned; update the logic in fetchSlackMentions (around
the calls to slackGet for "conversations.list" and "conversations.history") to
throw a descriptive Error instead of using break when resp.ok is false (include
resp.error or status info in the message), matching the auth.test error
behavior; ensure the thrown error propagates up so callers receive a failure
rather than incomplete tag data.
- Around line 67-71: The default period value of "all" causes getCutoffTs to
return null and triggers an unbounded full-workspace crawl (function getCutoffTs
and the code path that iterates channels/messages), which will exhaust Slack API
limits; fix by changing the default period from "all" to a bounded window such
as "weekly" in the request validation (where the default is set) and/or enforce
a maximum allowed window in validateGetSlackTagsQuery so getCutoffTs never
receives "all" for unauthenticated/public requests; alternatively implement
caching/persistence (store previously fetched mentions and perform incremental
updates) and add server-side protection (rate limiting or requiring auth) to
avoid walking all channels.

In `@lib/admins/coding-agent/validateGetSlackTagsQuery.ts`:
- Around line 36-40: The error response in the failure branch of
validateGetSlackTagsQuery.ts returns { status, error } but other branches use {
status, message }; change the NextResponse.json payload in the result.success
false branch (where firstError is extracted) to return { status: "error",
message: firstError.message } while preserving the 400 status and
getCorsHeaders() usage so the client sees a consistent error envelope; update
any local variable names if needed to match this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d09486ec-3d3e-45a3-9ffe-e34e37997dee

📥 Commits

Reviewing files that changed from the base of the PR and between 80069f8 and 87b62d1.

⛔ Files ignored due to path filters (2)
  • lib/admins/coding-agent/__tests__/getSlackTagsHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/admins/coding-agent/__tests__/validateGetSlackTagsQuery.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (4)
  • app/api/admins/coding-agent/slack-tags/route.ts
  • lib/admins/coding-agent/fetchSlackMentions.ts
  • lib/admins/coding-agent/getSlackTagsHandler.ts
  • lib/admins/coding-agent/validateGetSlackTagsQuery.ts

Comment on lines +13 to +18
export async function GET(request: NextRequest): Promise<NextResponse> {
return getSlackTagsHandler(request);
}

export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Please add endpoint coverage before merging.

I don't see tests in this change set for the success path, invalid period, auth rejection, Slack upstream failure, or the OPTIONS preflight. This endpoint is mostly orchestration, so a small route/handler test matrix will catch regressions quickly.

Based on learnings: "Write tests for new API endpoints covering all success and error paths."

If helpful, I can sketch the minimal test matrix for the route/handler split.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/admins/coding-agent/slack-tags/route.ts` around lines 13 - 18, The PR
lacks automated tests for the new Slack tags endpoint in route.ts (GET ->
getSlackTagsHandler and OPTIONS preflight); add a small test matrix that covers:
successful GET (mock auth to return a valid user and mock the Slack upstream to
return tags, assert JSON and status 200), invalid period parameter (call GET
with bad period, assert 400), auth rejection (mock auth to fail, assert 401/403
as implemented), Slack upstream failure (mock the upstream call used by
getSlackTagsHandler to return an error/timeout and assert the handler returns
the expected error/status), and the OPTIONS preflight (call OPTIONS and assert
status 204 and CORS headers via getCorsHeaders()). Use unit tests on
getSlackTagsHandler for orchestration logic and a lightweight route-level test
for OPTIONS; mock external deps (auth, Slack client, getCorsHeaders) and keep
tests minimal and focused.

Comment on lines +17 to +18
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add JSDoc for OPTIONS.

Line 17 is an exported route handler and currently has no docblock.

Based on learnings: "All API routes should have JSDoc comments."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/admins/coding-agent/slack-tags/route.ts` around lines 17 - 18, Add a
JSDoc block for the exported route handler OPTIONS explaining its purpose
(responds to preflight CORS requests), mention it returns a 204 No Content with
CORS headers, and annotate the return type (Promise<NextResponse>); place the
comment immediately above the export async function OPTIONS() declaration and
reference getCorsHeaders() in the description to clarify where headers come
from.

Comment on lines +67 to +71
function getCutoffTs(period: SlackTagsPeriod): number | null {
if (period === "all") return null;
const days = period === "daily" ? 1 : period === "weekly" ? 7 : 30;
return (Date.now() - days * 24 * 60 * 60 * 1000) / 1000;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and read the file
fd "fetchSlackMentions.ts" -t f

Repository: recoupable/api

Length of output: 103


🏁 Script executed:

# Check file size to determine best way to read
wc -l lib/admins/coding-agent/fetchSlackMentions.ts

Repository: recoupable/api

Length of output: 107


🏁 Script executed:

# Read the full file
cat -n lib/admins/coding-agent/fetchSlackMentions.ts

Repository: recoupable/api

Length of output: 6692


🏁 Script executed:

# Find and read validateGetSlackTagsQuery to verify the default period
fd "validateGetSlackTagsQuery*" -t f

Repository: recoupable/api

Length of output: 178


🏁 Script executed:

# Read validateGetSlackTagsQuery to see the default period
cat -n lib/admins/coding-agent/validateGetSlackTagsQuery.ts

Repository: recoupable/api

Length of output: 1832


🏁 Script executed:

# Check if there's a route handler that calls this function
fd "slack-tags" -t f

Repository: recoupable/api

Length of output: 40


🏁 Script executed:

# Check for any caching layer or memoization
rg "fetchSlackMentions" -A 3 -B 3

Repository: recoupable/api

Length of output: 7053


🏁 Script executed:

# Read the handler to see if there's any caching or timeout handling
cat -n lib/admins/coding-agent/getSlackTagsHandler.ts

Repository: recoupable/api

Length of output: 1601


Address the full workspace crawl risk in the default period="all" mode.

When getCutoffTs() returns null, the function walks through every accessible channel and all messages in each channel without time filtering (lines 115–174). Since period defaults to "all" (validateGetSlackTagsQuery.ts line 7), an unauthenticated request hits this expensive path immediately. On larger workspaces, this generates excessive Slack API calls that will exceed rate limits (Slack's web API allows ~20 requests/minute) or exceed request timeouts.

Before enabling this endpoint in production, implement caching/persistence (e.g., store mentions in the database and refresh incrementally) or change the default period to a bounded option like "weekly".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/admins/coding-agent/fetchSlackMentions.ts` around lines 67 - 71, The
default period value of "all" causes getCutoffTs to return null and triggers an
unbounded full-workspace crawl (function getCutoffTs and the code path that
iterates channels/messages), which will exhaust Slack API limits; fix by
changing the default period from "all" to a bounded window such as "weekly" in
the request validation (where the default is set) and/or enforce a maximum
allowed window in validateGetSlackTagsQuery so getCutoffTs never receives "all"
for unauthenticated/public requests; alternatively implement caching/persistence
(store previously fetched mentions and perform incremental updates) and add
server-side protection (rate limiting or requiring auth) to avoid walking all
channels.

Comment on lines +98 to +103
do {
const params: Record<string, string> = { types: "public_channel,private_channel", limit: "200" };
if (channelCursor) params.cursor = channelCursor;

const resp = await slackGet<ConversationsListResponse>("conversations.list", token, params);
if (!resp.ok) break;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n lib/admins/coding-agent/fetchSlackMentions.ts

Repository: recoupable/api

Length of output: 6692


Replace silent partial failures with explicit error handling to prevent incomplete analytics.

Lines 103 and 128 break on Slack API failures, allowing the function to return whatever tags were collected before the failure. This means incomplete data silently passes through, undercounting mentions for an analytics endpoint.

The error handling is also inconsistent—line 89 throws on auth.test failure, but conversations.list (103) and conversations.history (128) silently break. To maintain data integrity, propagate these failures as errors instead.

Suggested fix
-    if (!resp.ok) break;
+    if (!resp.ok) {
+      throw new Error(`Slack conversations.list failed: ${resp.error ?? "unknown error"}`);
+    }-      if (!history.ok) break;
+      if (!history.ok) {
+        throw new Error(
+          `Slack conversations.history failed for channel ${channel.id}: ${history.error ?? "unknown error"}`,
+        );
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/admins/coding-agent/fetchSlackMentions.ts` around lines 98 - 103, The
code currently treats Slack API failures in conversations.list and
conversations.history by breaking out of loops, allowing partial results to be
returned; update the logic in fetchSlackMentions (around the calls to slackGet
for "conversations.list" and "conversations.history") to throw a descriptive
Error instead of using break when resp.ok is false (include resp.error or status
info in the message), matching the auth.test error behavior; ensure the thrown
error propagates up so callers receive a failure rather than incomplete tag
data.

Comment on lines +36 to +40
if (!result.success) {
const firstError = result.error.issues[0];
return NextResponse.json(
{ status: "error", error: firstError.message },
{ status: 400, headers: getCorsHeaders() },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep the error envelope consistent with the other branches.

Line 39 returns { status, error }, but the 403 branch in lib/admins/validateAdminAuth.ts and the 500 branch in lib/admins/coding-agent/getSlackTagsHandler.ts both use { status, message }. Reusing message here keeps client-side error handling uniform.

As per coding guidelines: "Return consistent response formats."

Suggested fix
-      { status: "error", error: firstError.message },
+      { status: "error", message: firstError.message },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/admins/coding-agent/validateGetSlackTagsQuery.ts` around lines 36 - 40,
The error response in the failure branch of validateGetSlackTagsQuery.ts returns
{ status, error } but other branches use { status, message }; change the
NextResponse.json payload in the result.success false branch (where firstError
is extracted) to return { status: "error", message: firstError.message } while
preserving the 400 status and getCorsHeaders() usage so the client sees a
consistent error envelope; update any local variable names if needed to match
this change.

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