The Problem
Individual investors need a free, self‑hosted dashboard for real‑time stock quotes, watchlists, and price alerts. Commercial platforms charge per seat and lock data behind proprietary APIs, making it hard for hobbyists or small teams to experiment or integrate custom logic.
What This Does
OpenStock delivers a Next.js (App Router) web UI that pulls live market data from Finnhub, renders charts via TradingView widgets, and stores user watchlists and alerts in MongoDB. Core UI pieces live under components/ (e.g., components/watchlist/CreateAlertModal.tsx, components/watchlist/WatchlistTable.tsx). Server‑side actions such as price fetching and alert email dispatch are in lib/ (lib/actions/finnhub.actions.ts, lib/nodemailer/index.ts). Authentication is handled by Better Auth through the app/(auth)/ layout and pages. Docker support (Dockerfile, docker-compose.yml) lets the whole stack run locally or in production with a single command.
How It Is Wired
Execution begins at the Next.js server, bootstrapped by the entry point app/layout.tsx. The root layout imports the global stylesheet (app/globals.css) and renders shared UI (components/Header.tsx, components/Footer.tsx).
When a user navigates to /watchlist, app/(root)/watchlist/page.tsx renders the watchlist UI. It imports components/watchlist/WatchlistManager.tsx, which in turn pulls data via lib/actions/finnhub.actions.ts. That module calls Finnhub’s REST endpoints (API key supplied via environment variables) and returns JSON to the component.
Alert creation is handled by components/watchlist/CreateAlertModal.tsx. On submit it invokes lib/nodemailer/templates.ts (the oversized 1 039‑line file) to format an email, then passes the payload to lib/nodemailer/index.ts, which uses Nodemailer to send via Gmail SMTP.
API routes such as app/api/inngest/route.ts expose background jobs to Inngest; those jobs eventually call the same nodemailer utilities.
The import graph shows 79 internal modules with only 9 import edges and zero circular dependencies, so most files have limited blast radius. However, the most‑connected modules (scripts/verify-watchlist, components/watchlist/CreateAlertModal, components/watchlist/WatchlistManager) each import or are imported by multiple peers, meaning changes there ripple widely.
No CI pipeline is defined (.github/ contains only funding metadata), so automated testing or linting is absent.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/OpenStock.git
cd OpenStock
# Install npm dependencies
npm ci
# Build and run containers (MongoDB, Inngest, Next.js)
docker-compose up --build
Configuration: The Docker compose file expects a MongoDB URI and Finnhub API key; these are read from environment variables referenced in lib/actions/finnhub.actions.ts and lib/nodemailer/index.ts. Create a .env file at the repo root with the required keys (the repo does not ship a template, so you must add FINNHUB_API_KEY, MONGODB_URI, GMAIL_USER, GMAIL_PASS, etc., matching the variable names used in the source).
Running locally without Docker: after npm ci, start the dev server with npm run dev (the standard Next.js script defined in package.json). The server listens on the port configured in next.config.ts.
Real‑World Use
A fintech startup can fork OpenStock, replace the Finnhub client in lib/actions/finnhub.actions.ts with their own data provider, and keep the existing watchlist UI. Users authenticate via Better Auth, create a watchlist, and receive price‑threshold emails generated by lib/nodemailer/index.ts. Because the UI components and data layer are loosely coupled, the team can swap out the chart widget (components/TradingViewWidget.tsx) for a custom D3 chart with minimal impact.
Code Health & Issues
- High – Deep nesting – 29 occurrences (e.g.,
components/watchlist/CreateAlertModal.tsx,app/(auth)/layout.tsx,app/layout.tsx) with indentation depth up to 8, making logic hard to follow. - High – Duplicated code – Repeated 6‑line blocks across 6 files (
app/(auth)/sign‑in/page.tsx,app/(auth)/sign‑up/page.tsx,components/DonatePopup.tsx,components/ui/command.tsx, …). Consolidate into shared helpers. - Medium – High branching density –
lib/actions/finnhub.actions.tsandlib/utils.tscontain 76 branch points over 189 lines; consider extracting strategies or lookup tables. - Medium – Oversized file –
lib/nodemailer/templates.tsholds 1 039 lines; split by email type to reduce change impact. - SDLC gaps – No CI/CD configuration (
.github/lacks workflows). Tests exist (5 files) but are not integrated into an automated pipeline. - Repo hygiene – Dockerfile present, AGPL‑3.0 license included, lockfile (
package-lock.json) committed, no secrets detected.
The Bottom Line
OpenStock provides a functional, containerizable stock‑watchlist platform built with modern React/Next.js tooling. It is immediately runnable via Docker and offers a clear separation between UI, data fetching, and email alerts. However, the codebase suffers from deep nesting, duplicated snippets, and a monolithic email‑template module, which increase maintenance risk. The lack of CI automation further slows safe iteration. Suitable for teams that need a free starting point and are prepared to refactor the highlighted hotspots.