Full-stack dashboard for monitoring competitors, products, and market signals from a single interface. The project centralizes metrics, news, and relevant events to compare entities, detect changes, and expose them through a REST API and real-time updates.
Repository: https://github.com/caeher/competitive-intelligence-dashboard
This repository includes:
- A modular backend built with
Express + TypeScript + Prisma. - A frontend built with
React + Vite + Tailwind CSS. - JSON file-based configuration for entities, verticals, fetchers, scheduler, and thresholds.
- Persistence with Prisma and a SQLite database for the local workflow and Docker setup included in the repo.
Main use cases:
- Browse entities and their products.
- Compare metrics across multiple entities.
- Aggregate news and external signals from multiple sources.
- Expose system operational status and health.
Frontend (React + Vite)
|
| HTTP -> /api/v1
| WS -> /ws
v
Backend (Express + TypeScript)
|
+-> Prisma
+-> JSON config in backend/config/
+-> Scheduled jobs
+-> Alerts and events
| Path | Description |
|---|---|
backend/ |
API, domain modules, Prisma, configuration, and jobs |
backend/config/ |
Entities, verticals, fetchers, scheduler, and thresholds |
frontend/ |
React web application |
spec/ |
Project specifications and contracts |
presentation/ |
Five-slide PDF deck (competitive-intelligence-dashboard.pdf) + regen script at repo root |
CONTRIBUTING.md |
Contribution guide |
CODE_OF_CONDUCT.md |
Code of conduct |
SECURITY.md |
Vulnerability reporting process |
The dashboard is config-driven: swap Blockstream’s reference dataset for your own companies without changing application code.
- Verticals — Edit
backend/config/verticals.json. Each vertical needs a uniqueid,name, and optionaldisplayOrder. - Entities — Edit
backend/config/entities.json(see minimal skeletonbackend/config/entities.json.example). For each entity setname,isPrimary,verticals,newsKeywords, andidentifiers(GitHub org/repos,appStore.ios/appStore.android, website, Twitter, etc.). Addproductsfor SKU-level apps/repos. - Fetchers & schedule — Enable or disable sources in
backend/config/fetchers.jsonand tune cron expressions inbackend/config/scheduler.json. - Thresholds — Edit
backend/config/thresholds.jsonfor alert sensitivity. - Apply — Run
npm run prisma:push --prefix backend(or your migration workflow) and start the app; the backend syncs config into Prisma when core rows are missing.
Candidate surfacing from news uses optional candidates settings in backend/config/app.json (domainKeywords, mentionThreshold, confidenceThreshold).
| Layer | Technologies |
|---|---|
| Frontend | React 19, Vite 8, Tailwind CSS 4, TanStack Table, Recharts |
| Backend | Node.js, Express 4, TypeScript 5, Prisma 5, Zod, Pino, ws, node-cron |
| Database | SQLite in the repository's included setup |
| Containers | Docker, Docker Compose, Nginx |
- Node.js 20 or later
- npm 10 or later
- Docker Desktop + Docker Compose v2 if you plan to use containers
From the repository root:
npm install
npm run install:allIn PowerShell:
Copy-Item backend/.env.example backend/.envOn macOS or Linux:
cp backend/.env.example backend/.envnpm run prisma:generate --prefix backend
npm run prisma:push --prefix backendThe backend syncs and seeds base data from backend/config/*.json when it detects essential data is missing. If you need to force the seed manually:
npm run prisma:seed --prefix backendnpm run dev- Frontend:
http://localhost:5173 - Backend:
http://localhost:3001 - API:
http://localhost:3001/api/v1 - WebSocket:
ws://localhost:3001/ws
Deliverable deck: after npm install at the repo root, run npm run presentation:pdf to regenerate presentation/competitive-intelligence-dashboard.pdf (5 slides: problem, architecture, demo notes, implemented vs SPEC, next steps).
Useful notes:
- In local development, Vite proxies
/apitohttp://localhost:3001. - The backend exposes a root endpoint at
/and health routes under/api/v1/healthand/api/v1/status.
The repository's Docker configuration starts:
backendon Node.js with Prisma and SQLite persisted in a Docker volume.frontendserved with Nginx, proxying requests to the backend API and WebSocket.
For local machine access through published ports, use the dedicated override file docker-compose.local.yml.
If you want to enable external integrations, you can define these variables before starting the containers:
GITHUB_TOKENNEWS_API_KEY
You can export them in your terminal or define them in a root .env file so Docker Compose reads them automatically.
docker compose up --buildTo run in the background:
docker compose up --build -dFor local access from your PC, start Docker Compose with the local override:
docker compose -f docker-compose.yml -f docker-compose.local.yml up --buildBackground mode:
docker compose -f docker-compose.yml -f docker-compose.local.yml up --build -dThe local override publishes only on 127.0.0.1:
- Frontend:
127.0.0.1:3000 -> 80 - Backend:
127.0.0.1:3001 -> 3001
You can override those host ports with environment variables:
$env:LOCAL_FRONTEND_PORT = "8080"
$env:LOCAL_BACKEND_PORT = "3101"
docker compose -f docker-compose.yml -f docker-compose.local.yml up --build- Web app:
http://localhost:3000 - API through Nginx proxy:
http://localhost:3000/api/v1 - WebSocket through Nginx proxy:
ws://localhost:3000/ws - Backend direct access when using the local override:
http://localhost:3001
Important:
docker-compose.ymlkeeps backend and frontend on the internal Docker network.docker-compose.local.ymlis the local-only override that publishes frontend and backend on127.0.0.1.- The SQLite database is persisted in the
backend-datavolume.
View logs:
docker compose logs -fStop services:
docker compose downAlso remove the data volume:
docker compose down -v| Script | Description |
|---|---|
npm run dev |
Starts backend and frontend in parallel |
npm run dev:backend |
Starts only the backend |
npm run dev:frontend |
Starts only the frontend |
npm run build |
Builds backend and frontend |
npm run install:all |
Installs dependencies for backend/ and frontend/ |
| Script | Description |
|---|---|
npm run dev --prefix backend |
Runs the backend in development mode |
npm run build --prefix backend |
Compiles TypeScript |
npm run start --prefix backend |
Runs the compiled backend |
npm run typecheck --prefix backend |
Checks types without emitting files |
npm run test --prefix backend |
Runs backend tests |
npm run prisma:generate --prefix backend |
Generates the Prisma client |
npm run prisma:push --prefix backend |
Syncs the schema to the database |
npm run prisma:migrate --prefix backend |
Runs development migrations |
npm run prisma:seed --prefix backend |
Seeds data from configuration |
| Script | Description |
|---|---|
npm run dev --prefix frontend |
Starts Vite |
npm run build --prefix frontend |
Builds the frontend |
npm run lint --prefix frontend |
Runs ESLint |
npm run preview --prefix frontend |
Serves the Vite build for validation |
The base file is backend/.env.example.
| Variable | Default | Description |
|---|---|---|
NODE_ENV |
development |
Execution environment |
PORT |
3001 |
Backend HTTP port |
DATABASE_URL |
file:./dev.db |
Prisma connection string |
LOG_LEVEL |
info |
Log level |
CORS_ORIGIN |
http://localhost:5173 |
Allowed CORS origin |
ENABLE_CRON |
true |
Enables scheduled jobs |
MAIL_PROVIDER |
console |
Mail provider |
GITHUB_TOKEN |
empty | Optional token for the GitHub API |
NEWS_API_KEY |
empty | Optional API key for NewsAPI |
/entities/entities/:entityId/entities/:entityId/products/:productId/compare
All business routes live under /api/v1.
Examples:
GET /api/v1/healthGET /api/v1/statusGET /api/v1/entitiesGET /api/v1/entities/:entityIdGET /api/v1/entities/datatableGET /api/v1/entities/selectGET /api/v1/compareGET /api/v1/feedGET /api/v1/feed/:feedItemIdPATCH /api/v1/feed/:feedItemIdGET /api/v1/alertsGET /api/v1/alerts/:alertIdPOST /api/v1/alerts/:alertId/acknowledgeGET /api/v1/candidatesGET /api/v1/candidates/:candidateIdPOST /api/v1/candidates/:candidateId/confirmPOST /api/v1/candidates/:candidateId/dismissGET /api/v1/fetchers/healthPOST /api/v1/fetchers/:sourceId/refresh
GET /api/v1/compare?entities=blockstream,trezor&metrics=stars,forks,commits30d
GET /api/v1/entities?sort=stars&direction=desc&page=1&limit=10
GET /api/v1/entities/datatable?draw=1&start=0&length=20&search[value]=block
Response shape:
{
"draw": 1,
"recordsTotal": 30,
"recordsFiltered": 3,
"data": [...]
}GET /api/v1/entities/select?search=block&page=1&limit=20
Response shape:
{
"items": [{ "value": "blockstream", "label": "Blockstream" }],
"pagination": { "page": 1, "limit": 20, "hasMore": false, "total": 1 }
}GET /api/v1/feed?entityId=blockstream&itemType=article&from=2026-04-01T00:00:00Z&page=1&limit=20
GET /api/v1/alerts?severity=high&isAcknowledged=false&page=1&limit=10
POST /api/v1/alerts/{alertId}/acknowledge
Content-Type: application/json
{ "acknowledgedBy": "analyst@example.com" }
GET /api/v1/candidates?status=pending&sort=confidenceScore&direction=desc
POST /api/v1/candidates/{candidateId}/confirm
Content-Type: application/json
{ "reviewedBy": "analyst", "reviewNotes": "Valid competitor" }
POST /api/v1/candidates/{candidateId}/dismiss
Content-Type: application/json
{ "reviewedBy": "analyst", "reviewNotes": "Not relevant" }
Connect to ws://localhost:3001/ws and subscribe to channels:
{ "type": "subscribe", "channels": ["alerts", "feed", "candidates"] }The server broadcasts events on these channels:
alerts— New alert detections (type:alert)feed— New feed items ingested (type:feed_item)candidates— New competitor candidates detected (type:metric_update)
Before contributing, review:
Recommended workflow summary:
- Open an issue with context, expected scope, and impacted areas.
- Propose specification changes first if behavior is going to change.
- Keep code, contracts, configuration, and documentation in sync.
- Run local validations before opening a PR.
- Submit small, clear PRs with explicit risks.
- Use branches in the format
feature/<topic>,fix/<topic>, ordocs/<topic>. - Use PR titles in the format
[area] short summary. - Keep behavior configuration-driven when the repo already models it that way.
- Avoid hardcoding ids, entity names, or thresholds in code.
- If you change contracts, review the impact on backend, frontend, Prisma, and documentation.
npm run typecheck --prefix backend
npm run test --prefix backend
npm run lint --prefix frontend
npm run buildIf you change Prisma:
npm run prisma:generate --prefix backend
npm run prisma:push --prefix backend- Functional configuration:
backend/config/ - Specifications:
spec/ - Entity contract:
spec/ENTITY-CONFIG.md - Implementation status and future development:
spec/SPEC.md - Contribution guide:
CONTRIBUTING.md
- Verify that
backend/.envexists. - Check that
DATABASE_URLhas a valid value. - Restart
npm run dev.
Run again:
npm run prisma:generate --prefix backend
npm run prisma:push --prefix backend- Confirm that the backend is running at
http://localhost:3001. - Check
CORS_ORIGINinbackend/.env. - In Docker, use the app from
http://localhost:3000, not directly from the backend container.
This project is distributed under the license included in LICENSE.