refractor: move all api endpoints from dashboard to api service#21
refractor: move all api endpoints from dashboard to api service#21Adhish-Krishna wants to merge 11 commits into
Conversation
…not needed. All requests route to the API service
…d api calls in template and send section
…in listNotifications
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 813865fb85
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| client_id: clientId, | ||
| channel: selectedChannels, | ||
| recipient: { ...recipientData }, | ||
| webhook_url: `http://${WEBHOOK_HOST}:${WEBHOOK_PORT}/api/webhook` |
There was a problem hiding this comment.
Keep webhook URL injection server-side
When dashboard send requests are now proxied straight to the core API, this webhook_url is trusted from the browser instead of being overwritten by the removed /api/send server route. In any authenticated dashboard session, a tampered request can redirect delivery-status callbacks to an arbitrary URL or make them fail; the proxy/server should inject the configured callback URL rather than accepting it from the client. The same issue exists in the batch form.
Useful? React with 👍 / 👎.
…d request management - Replaced fetch-based API request implementation with Axios. - Added centralized error handling through Axios interceptors. - Introduced ApiError class for better error representation. - Updated service modules (auth, notification, template, alert, plugin, admin channel, dashboard) to utilize the new Axios-based client. - Enhanced AdminChannelFormData to include a config field and added AdminChannelProviderMeta interface for provider metadata. - Updated package.json and package-lock.json to include Axios as a dependency.
There was a problem hiding this comment.
Pull request overview
This PR migrates “dashboard-owned” API functionality (notifications management, alerts, dashboard analytics, and admin-channel CRUD) into the core Express API service, then updates the Next.js dashboard and the MCP server to call the core API directly with centralized client logic and consistent authentication.
Changes:
- Added new authenticated core API routes/controllers for notifications management, alerts, and dashboard analytics, and wired them into
src/api/server.ts. - Refactored the dashboard to remove DB models/Next API routes and instead call the core API through a centralized Axios client + middleware proxy.
- Updated the MCP server to remove the Dashboard URL requirement (v1.1.0) and route requests to the core API with auth headers.
Reviewed changes
Copilot reviewed 67 out of 69 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/integration/api/notification.controller.test.ts | Updates integration test import to the renamed notification controller module. |
| tests/integration/api/dashboard_api.test.ts | Adds integration tests for the migrated dashboard-related core API routes (notifications mgmt, alerts, admin-channels, dashboard analytics). |
| src/api/server.ts | Registers new authenticated routers for notifications mgmt, alerts, and dashboard analytics; adds auth to plugins/admin-channels. |
| src/api/routes/notifications-management.routes.ts | New router for CRUD-ish notification management endpoints. |
| src/api/routes/notification.routes.ts | Updates controller import path to the renamed notification controller. |
| src/api/routes/dashboard.routes.ts | New router exposing dashboard analytics endpoints from the core API. |
| src/api/routes/alerts.routes.ts | New router exposing alerts endpoints from the core API. |
| src/api/routes/admin-channels.routes.ts | Extends router with admin-channel CRUD endpoints. |
| src/api/controllers/notifications-management.controller.ts | New controller implementing notification listing/recent/detail/delete/retry on the core API. |
| src/api/controllers/notification.controller.ts | New notification ingest controller file (single + batch). |
| src/api/controllers/dashboard.controller.ts | New controller implementing /api/dashboard/stats and /api/dashboard/trends in core API. |
| src/api/controllers/alerts.controller.ts | New controller implementing alerts list/dismiss/resolve/bulk-resolve in core API. |
| src/api/controllers/admin-channels-crud.controller.ts | New controller implementing admin-channel CRUD in core API (with config encryption). |
| packages/mcp-server/src/auth.ts | Removes Dashboard URL requirement from credential extraction and stdio creds (still needs minor cleanup). |
| packages/mcp-server/src/api-client.ts | Routes “dashboard” client calls to core URL and ensures auth header is sent for /api/plugins. |
| packages/mcp-server/README.md | Documents the v1.1 change: Dashboard URL header/env no longer required. |
| packages/mcp-server/package.json | Bumps MCP server package version to 1.1.0. |
| dashboard/package.json | Adds Axios dependency and removes mongoose dependency from dashboard. |
| dashboard/package-lock.json | Updates lockfile for removed mongoose + added axios (and related dependency graph changes). |
| dashboard/middleware.ts | Proxies non-local /api/* requests to the core API and injects the Bearer API key header. |
| dashboard/lib/utils.ts | Extends runtime config typing (basePath + webhook host/port support). |
| dashboard/lib/types.ts | Adjusts admin-channel form typing to support provider-driven credential schemas (config). |
| dashboard/lib/models/system-config.ts | Removes dashboard DB model (dashboard no longer connects directly to MongoDB). |
| dashboard/lib/models/outbox.ts | Removes dashboard outbox DB model. |
| dashboard/lib/models/notification.ts | Removes dashboard notification DB model. |
| dashboard/lib/models/alert.ts | Removes dashboard alert DB model. |
| dashboard/lib/models/admin-channel.ts | Removes dashboard admin-channel DB model. |
| dashboard/lib/encryption.ts | Removes dashboard-side encryption utilities (moved to core service). |
| dashboard/lib/db.ts | Removes dashboard MongoDB connection logic. |
| dashboard/lib/api-config.ts | Simplifies dashboard API config exports to core base URL + API key. |
| dashboard/lib/api-client.ts | Adds centralized Axios client, interceptors, and typed service modules for dashboard usage. |
| dashboard/entrypoint.sh | Adjusts comments/intent toward runtime configuration injection. |
| dashboard/components/send/single-notification-form.tsx | Switches to centralized client services; currently hard-codes webhook URL (needs follow-up). |
| dashboard/components/send/batch-notification-form.tsx | Switches to centralized client services; currently hard-codes webhook URL (needs follow-up). |
| dashboard/components/logout-button.tsx | Uses authService.logout() instead of manual fetch. |
| dashboard/components/layout/dashboard-layout.tsx | Uses Axios client fetcher for SWR instead of raw fetch wrapper. |
| dashboard/components/dashboard/stats-cards.tsx | Uses Axios client fetcher for SWR. |
| dashboard/components/dashboard/recent-activity.tsx | Uses Axios client fetcher for SWR. |
| dashboard/components/auth-provider.tsx | Uses authService.getSession() and authService.logout(). |
| dashboard/components/admin-alerts/add-channel-dialog.tsx | Uses adminChannelService for providers/test/create and improves error handling. |
| dashboard/app/templates/page.tsx | Uses Axios client fetcher and templateService for deletes/details. |
| dashboard/app/templates/editor/page.tsx | Uses Axios client fetcher + templateService for get/create/update. |
| dashboard/app/plugins/page.tsx | Uses Axios client fetcher and adds external docs link for plugin install guidance. |
| dashboard/app/payload-studio/page.tsx | Uses Axios client fetcher and adds external docs link for plugin install guidance. |
| dashboard/app/login/page.tsx | Uses authService.login() and improved error handling. |
| dashboard/app/failed/page.tsx | Uses Axios fetcher + notificationService.retry() for retry action. |
| dashboard/app/events/page.tsx | Uses Axios client fetcher for SWR. |
| dashboard/app/events/[id]/page.tsx | Uses Axios fetcher + notificationService.retry/delete() for actions. |
| dashboard/app/api/templates/route.ts | Removes dashboard Next API proxy route (now handled via middleware proxy + direct core API). |
| dashboard/app/api/templates/[template_id]/route.ts | Removes dashboard Next API proxy route. |
| dashboard/app/api/send/route.ts | Removes dashboard send proxy route (previously injected webhook URL). |
| dashboard/app/api/plugins/route.ts | Removes dashboard Next API proxy route. |
| dashboard/app/api/notifications/route.ts | Removes dashboard DB-backed notifications list endpoint. |
| dashboard/app/api/notifications/recent/route.ts | Removes dashboard DB-backed recent notifications endpoint. |
| dashboard/app/api/notifications/[id]/route.ts | Removes dashboard DB-backed notification detail/delete endpoint. |
| dashboard/app/api/notifications/[id]/retry/route.ts | Removes dashboard DB-backed retry endpoint. |
| dashboard/app/api/dashboard/trends/route.ts | Removes dashboard DB-backed dashboard trends endpoint. |
| dashboard/app/api/dashboard/stats/route.ts | Removes dashboard DB-backed dashboard stats endpoint. |
| dashboard/app/api/alerts/route.ts | Removes dashboard DB-backed alerts list endpoint. |
| dashboard/app/api/alerts/bulk-resolve/route.ts | Removes dashboard DB-backed alerts bulk-resolve endpoint. |
| dashboard/app/api/alerts/[id]/route.ts | Removes dashboard DB-backed alerts dismiss endpoint. |
| dashboard/app/api/alerts/[id]/resolve/route.ts | Removes dashboard DB-backed alerts resolve endpoint. |
| dashboard/app/api/admin-channels/test/route.ts | Removes dashboard Next API proxy route for admin-channel test. |
| dashboard/app/api/admin-channels/route.ts | Removes dashboard DB-backed admin-channels list/create endpoint. |
| dashboard/app/api/admin-channels/providers/route.ts | Removes dashboard Next API proxy route for providers. |
| dashboard/app/api/admin-channels/[id]/route.ts | Removes dashboard DB-backed admin-channel get/update/delete endpoint. |
| dashboard/app/analytics/page.tsx | Uses Axios client fetcher for SWR. |
| dashboard/app/alerts/page.tsx | Uses alertService methods for list/resolve/bulkResolve/delete. |
| dashboard/app/admin-alerts/page.tsx | Uses Axios fetcher + adminChannelService.update/delete() for actions. |
Files not reviewed (1)
- dashboard/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| channel: selectedChannels, | ||
| recipient: { ...recipientData }, | ||
| //hard-coded the webhook url as the simplest solution. This url may not be correct all the times. Nop issues, the worker just fire and forgets while sending webhook, it does not retires | ||
| webhook_url: "http://localhost:3002/api/webhook" | ||
| }; |
| const payload: Record<string, any> = { | ||
| type: "batch", | ||
| client_id: clientId, | ||
| channel: selectedChannels, | ||
| recipients: processedRecipients, | ||
| //hard-coded the webhook url as the simplest solution. This url may not be correct all the times. Nop issues, the worker just fire and forgets while sending webhook, it does not retires | ||
| webhook_url: "http://localhost:3002/api/webhook" | ||
| }; |
| export const alertService = { | ||
| list: (page: number, limit: number, type?: string): Promise<{ | ||
| alerts: Alert[]; | ||
| count: number; | ||
| byType: Record<string, number>; | ||
| page: number; | ||
| limit: number; | ||
| totalPages: number; | ||
| }> => | ||
| apiClient.get('/api/alerts', { | ||
| params: { page, limit, type }, | ||
| }), | ||
| get: (id: string): Promise<Alert> => | ||
| apiClient.get(`/api/alerts/${id}`), | ||
| resolve: (id: string, payload: { appendWarning?: boolean }): Promise<{ success: boolean }> => | ||
| apiClient.post(`/api/alerts/${id}/resolve`, payload), | ||
| bulkResolve: (payload: { appendWarning?: boolean; limit?: number }): Promise<{ success: boolean; message: string }> => | ||
| apiClient.post('/api/alerts/bulk-resolve', payload), | ||
| delete: (id: string): Promise<{ success: boolean }> => | ||
| apiClient.delete(`/api/alerts/${id}`), | ||
| }; |
| export const dashboardService = { | ||
| getStats: (): Promise<DashboardStats> => apiClient.get('/api/dashboard/stats'), | ||
| getTrends: (hours?: number): Promise<TrendDataPoint[]> => | ||
| apiClient.get('/api/dashboard/trends', { params: { hours } }), | ||
| }; |
| const apiKey = req.headers['x-simplens-api-key'] as string | undefined; | ||
| const coreUrl = req.headers['x-simplens-core-url'] as string | undefined; | ||
| const dashboardUrl = req.headers['x-simplens-dashboard-url'] as string | undefined; | ||
|
|
| return { | ||
| apiKey: SIMPLENS_API_KEY, | ||
| coreUrl: SIMPLENS_CORE_URL, | ||
| dashboardUrl: SIMPLENS_DASHBOARD_URL, | ||
| coreUrl: SIMPLENS_CORE_URL | ||
| }; |
| } else if (content.message) { | ||
| content.message = String(content.message) + warningMessage; | ||
| } | ||
| } |
| } else if (content.message) { | ||
| content.message = String(content.message) + warningMessage; | ||
| } | ||
| } |
| recipient: { ...recipientData }, | ||
| //hard-coded the webhook url as the simplest solution. This url may not be correct all the times. Nop issues, the worker just fire and forgets while sending webhook, it does not retires | ||
| webhook_url: "http://localhost:3002/api/webhook" | ||
| }; |
| const payload: Record<string, any> = { | ||
| type: "batch", | ||
| client_id: clientId, | ||
| channel: selectedChannels, | ||
| recipients: processedRecipients, | ||
| //hard-coded the webhook url as the simplest solution. This url may not be correct all the times. Nop issues, the worker just fire and forgets while sending webhook, it does not retires | ||
| webhook_url: "http://localhost:3002/api/webhook" | ||
| }; |
| # Get configuration from environment | ||
| BASE_PATH="${BASE_PATH:-}" | ||
|
|
| for (const field of schema) { | ||
| const value = config[field.name]; | ||
| if (field.required && (!value || value.trim() === '')) { | ||
| errors.push(`${field.label} is required`); | ||
| continue; | ||
| } | ||
| if (value && field.pattern) { | ||
| const regex = new RegExp(field.pattern); | ||
| if (!regex.test(value)) { | ||
| errors.push(`${field.label} has invalid format`); | ||
| } | ||
| } | ||
| } |
| for (const field of schema) { | ||
| const value = body.config[field.name]; | ||
| if (field.required && (!value || value.trim() === '')) { | ||
| errors.push(`${field.label} is required`); | ||
| continue; | ||
| } | ||
| if (value && field.pattern) { | ||
| const regex = new RegExp(field.pattern); | ||
| if (!regex.test(value)) { | ||
| errors.push(`${field.label} has invalid format`); | ||
| } | ||
| } | ||
| } |
| // Aggregate by hour for 24h, by day for 7d/30d | ||
| const groupBy = period === '24h' | ||
| ? { $hour: '$created_at' } | ||
| : { $dayOfYear: '$created_at' }; | ||
|
|
Key changes