Skip to content

Follow-up to PR #111: Add backend/README.md and implement SSE authentication alignment#114

Merged
csecrestjr merged 1 commit into
mainfrom
copilot/fix-e12c07e1-11de-4808-bb17-76b0b9da7626
Sep 13, 2025
Merged

Follow-up to PR #111: Add backend/README.md and implement SSE authentication alignment#114
csecrestjr merged 1 commit into
mainfrom
copilot/fix-e12c07e1-11de-4808-bb17-76b0b9da7626

Conversation

Copilot AI commented Sep 13, 2025

Copy link
Copy Markdown

This PR addresses the issues from PR #111 which was merged but contained no actual file changes. This follow-up ensures the backend documentation and SSE authentication improvements are properly implemented.

Changes Made

1. Added backend/README.md

Created comprehensive documentation for the Fruitful Backend (Agrinet Platform) including:

  • Technology stack overview (Node.js, Express, AWS DynamoDB, SSE, BullMQ)
  • Quickstart and Docker setup instructions
  • Environment configuration requirements
  • Complete API routing documentation
  • Data model descriptions
  • Chat & streaming endpoints reference
  • Security and authentication details
  • Testing and contribution guidelines

2. Implemented SSE Authentication Alignment

Enhanced backend/server.js with proper authentication for Server-Sent Events endpoints:

// New withSseAuth helper function
function withSseAuth(handler) {
  return (req, res) => {
    // Extract API key from query parameters (x-api-key preferred, api_key for backward compatibility)
    const qKey = req.query['x-api-key'] || req.query['api_key'];
    
    // If API key is provided in query, set it in headers for authMiddleware
    if (qKey && !req.headers['x-api-key']) {
      req.headers['x-api-key'] = qKey;
    }
    
    // Use authMiddleware to validate the request
    authMiddleware(req, res, (err) => {
      if (err) {
        res.status(401).end();
        return;
      }
      handler(req, res);
    });
  };
}
  • Wrapped /events and /stream/:conversationId endpoints with authentication
  • Supports both x-api-key and api_key query parameters for backward compatibility
  • Returns 401 Unauthorized for invalid/missing API keys
  • Maintains existing SSE functionality when properly authenticated

3. Frontend Compatibility Verified

Confirmed that frontend/chat-ui/src/components/ChatWindow.jsx already uses the stream() helper from the API module, which automatically appends the x-api-key query parameter. No frontend changes were required.

Testing

  • All existing tests pass (5/5)
  • Manual verification confirms SSE authentication works correctly:
    • Endpoints reject requests without valid API keys (401)
    • Endpoints accept requests with valid x-api-key or api_key parameters
    • SSE connections establish properly when authenticated

Security Impact

This change improves security by ensuring that SSE endpoints now require proper authentication, preventing unauthorized access to real-time chat streams while maintaining backward compatibility with existing clients.

Fixes the missing documentation and authentication issues identified as follow-up work to PR #111.

This pull request was created as a result of the following prompt from Copilot chat.

Follow-up to PR #111: The PR was merged but appears to contain no file changes (backend/README.md is missing on main). This PR will actually add the documentation file and ensure the SSE authentication alignment is implemented in code.

Objectives

  1. Add backend/README.md with the essentials
  • Use the exact content below.
# Fruitful Backend (Agrinet Platform)

This backend powers Agrinet services using Node.js, Express, AWS DynamoDB, and Server‑Sent Events (SSE) for streaming chat updates.

## Overview

- Runtime: Node.js + Express
- Data: AWS DynamoDB (DocumentClient)
- Streaming: SSE (`/events`, `/stream/:conversationId`)
- Auth: JWT middleware; API Key support for some endpoints
- Queues: BullMQ for SMS and background jobs
- Uploads: Files stored under `backend/uploads` served at `/uploads`

## Quickstart

```bash
cd backend
npm install
node server.js
```

Docker-based local dev with DynamoDB Local:
```bash
docker compose up --build
```

The API typically runs on port 5000 (see docker-compose).

## Environment

Required (or set via `.env`):
- AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DYNAMODB_ENDPOINT (for local)
- JWT_SECRET
- TWILIO_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER (for SMS; optional TWILIO_STATUS_CALLBACK_URL)
- STRIPE_KEY (if deposits enabled)

## Routing

Main mounts (see `server.js`):
- `/health` – health check
- `/uploads/*` – static uploads
- `/events` – SSE broadcast channel
- `/stream/:conversationId` – SSE per‑conversation stream

Domain routes (when not in minimal mode):
- `/``routes/api.js` (composed transaction + notification)
- `/api/auth` → auth routes
- `/api/keys` → key management
- `/api/contracts` → contracts
- `/api/admin` → admin
- `/api/marketplace` → marketplace
- `/users` → user management
- `/products` → products
- `/broadcast` → broadcasts
- `/sms` → SMS webhooks
- `/cart`, `/orders`, `/subscriptions`
- `/conversations`, `/messages`
- `/inventory`, `/api/location`
- `/federation`, `/trends`
- `/deposit` (optional; guarded by auth)

## Data Model (DynamoDB tables)

- `Users`, `Keys`
- `Listings`, `Contracts`, `Transactions`
- `Conversations`, `Messages`
- `Notifications`
- `Inventory`
- `AgriculturalData` (for price/info lookups)

Each model file exports a `*_TABLE_NAME` and an item builder, e.g.:
- `models/user.js``USER_TABLE_NAME`, `createUserItem()`
- `models/transaction.js``TRANSACTION_TABLE_NAME`, `createTransactionItem()`

## Chat & Streaming

Endpoints (used by the Chat UI):
- `GET /conversations` → list
- `POST /conversations` → create
- `PUT /conversations/:id` → rename
- `POST /conversations/:id/pin` → toggle pin
- `DELETE /conversations/:id` → delete
- `GET /messages/:conversationId` → list messages
- `POST /messages/:conversationId` → send message (optionally with file)
- `GET /stream/:conversationId` (SSE) → events:
  - `token`: `{ id, token }`
  - `message`: `{ message }`

Server emitters (global):
- `emitToken(conversationId, id, token)`
- `emitMessage(conversationId, message)`

## Transactions & Notifications

- `POST /api/transactions`:
  - Writes item to `Transactions`
  - Writes `Notifications` item for buyer
  - Enqueues a “ping” job
  - May broadcast SSE events

## Security & Auth

- CORS: restricted to `https://www.ntari.org`
- JWT: middleware enforces authorization on protected routes
- API Key for SSE: the Chat UI passes an API key as `x-api-key` query parameter for SSE requests; the server accepts `x-api-key` (and also `api_key` for backward compatibility) on `/events` and `/stream/:conversationId`.

## Jobs & SMS

- `bull/smsQueue.js`: BullMQ worker that sends SMS via Twilio
- `routes/smsRoutes.js`: webhooks for incoming/status
- Background workers in Docker Compose:
  - `federation-sync`, `key-expiry-cleaner`

## Useful Paths

- Entry: `backend/server.js`
- Models: `backend/models/*`
- Routes: `backend/routes/*`
- Utils: `backend/utils/*`
- Queues: `backend/bull/*`
- Uploads: `backend/uploads`

## Testing

See `backend/package.json` for Jest config and tests. Add route/model tests under `__tests__/`.

---
Contributions welcome! Please keep `server.js` slim by adding routes and logic in domain folders.
  1. Align SSE authentication in backend/server.js
  • Wrap /events and /stream/:conversationId handlers with a withSseAuth helper that:
    • Reads req.query['x-api-key'] or req.query['api_key'].
    • If present, populates req.headers['x-api-key'] before calling authMiddleware.
    • On failure, responds 401 and ends the response.
  • Keep existing SSE logic intact otherwise.
  • Add comments documenting the behavior and backward compatibility.

Implementation sketch:

function withSseAuth(handler) {
  return (req, res) => {
    const qKey = req.query['x-api-key'] || req.query['api_key'];
    if (qKey && !req.headers['x-api-key']) req.headers['x-api-key'] = qKey;
    authMiddleware(req, res, (err) => {
      if (err) {
        res.status(401).end();
        return;
      }
      handler(req, res);
    });
  };
}

app.get('/events', withSseAuth((req, res) => { /* existing SSE setup */ }));
app.get('/stream/:conversationId', withSseAuth((req, res) => { /* existing SSE setup */ }));
  1. Frontend: ensure ChatWindow.jsx uses the stream() helper
  • File: frontend/chat-ui/src/components/ChatWindow.jsx
  • Import stream from ../api and call const events = stream(/stream/${id});.
  • Remove any manual api_key parameter usage in this component (the helper appends x-api-key).

Acceptance criteria

  • backend/README.md is present on main after merge.
  • SSE endpoints authorize via x-api-key (or fallback api_key) query param and return 401 when invalid/missing.
  • ChatWindow.jsx uses stream() without hardcoding api_key.
  • No changes to CORS or unrelated routes.

Notes


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@csecrestjr
csecrestjr marked this pull request as ready for review September 13, 2025 15:51
@csecrestjr
csecrestjr merged commit 085f2e8 into main Sep 13, 2025
2 checks passed
Copilot AI changed the title [WIP] Add backend/README.md and align SSE auth (follow-up to #111) Follow-up to PR #111: Add backend/README.md and implement SSE authentication alignment Sep 13, 2025
Copilot AI requested a review from csecrestjr September 13, 2025 15:59
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