Skip to content

Repository files navigation

StockLens compass rose logo

StockLens

Track real-time stock prices, get personalized alerts, and explore detailed company insights.

A full-stack stock market dashboard built with Next.js 16, MongoDB, Better Auth, Inngest, and TradingView.



Next.js React TypeScript Tailwind CSS MongoDB Inngest Vercel License: MIT

πŸš€ Visit Live Β β€’Β  πŸ› Report a Bug Β β€’Β  ✨ Request a Feature

Table of Contents

πŸ”­ Overview

StockLens is a market-tracking web application that gives retail investors a single place to watch the market, research individual companies, and receive tailored email updates. It combines live market widgets from TradingView, company/search data from Finnhub, secure email-and-password authentication via Better Auth, and event-driven background workflows powered by Inngest - including an AI-generated personalized welcome email.

Alongside US markets, StockLens covers the Colombo Stock Exchange (CSE) in Sri Lanka. Indices, movers, sector performance and every listed security are read live from the exchange's own public JSON API

The app is server-first (Next.js App Router + React Server Components), styled with Tailwind CSS v4 and shadcn/ui, and deploys to Vercel.

Disclaimer: StockLens is an informational tool. It does not provide investment, financial, or trading advice.


πŸš€ Try It

No sign-up required. On the sign-in page, click "Explore the demo" to enter with a temporary guest account and browse the full app.

Under the hood this uses Better Auth's anonymous plugin to create a sandboxed, flagged session (isAnonymous: true). Demo accounts are rate-limited (5 per IP / hour), blocked from write actions via a requireRealUser() guard, and purged automatically after 24 hours by the cleanup-demo-users Inngest cron. If you later sign up for real, the guest session is linked to your new account.


✨ Features

  • πŸ” Authentication :- Email/password sign-up and sign-in with Better Auth, backed by MongoDB. Session-aware layouts automatically redirect authenticated users away from auth pages and unauthenticated users to sign-in.

  • πŸ§ͺ One-click Demo Mode :- A "Try the demo" button spins up a throwaway anonymous session (Better Auth anonymous plugin) so visitors can explore without signing up; guest accounts are rate-limited and swept hourly by an Inngest cron.

  • πŸ“Š Market Dashboard :- Live Market Overview, Stock Heatmap, Top Stories, and Market Data widgets embedded from TradingView.

  • 🏒 Stock Details Page :- Symbol info, advanced candlestick chart, baseline chart, technical-analysis gauge, company profile, and company financials for any ticker.

  • ⭐ Watchlist :- Follow any company and it is saved to your account

  • πŸ‡±πŸ‡° Colombo Stock Exchange :- A full Sri Lanka section reading live from the CSE's own JSON API at cse.lk: ASPI and S&P SL20 index cards, market status, turnover and trade counts, top gainers / losers / most active, sector indices, and every listed security in one sortable, searchable table. Each ticker gets a /cse/[symbol] page with company info, day and 52-week price ranges, and recent trades. CSE symbols are merged into ⌘K search next to US results and can be saved to the same watchlist, priced in LKR. Responses are cached server-side (60s for live data, 1h for reference data), and one market-wide tradeSummary feed backs the table, search and watchlist hydration - so there is no per-symbol fan-out against the exchange.

  • πŸ”Ž Command-Palette Search :- ⌘K / Ctrl+K search dialog that queries Finnhub (US) and the CSE feed (Sri Lanka) in parallel and interleaves the two, with a debounced input and a fallback list of popular stocks.

  • βœ‰οΈ Transactional Email :- HTML email templates (welcome, news summary, price/volume alerts, inactive-user reminder) delivered through Nodemailer.

  • πŸ€– AI-Personalized Onboarding :- On sign-up, an Inngest function calls an Anthropic model (claude-haiku-4-5) to generate a welcome-email intro tailored to the user's stated investment goals, risk tolerance, and preferred industry.


🧰 Tech Stack

Layer Technology
Framework Next.js 16 (App Router, RSC, Turbopack), React 19
Language TypeScript 5
Styling Tailwind CSS v4, shadcn/ui, Base UI, tw-animate-css
UI / UX lucide-react, Sonner (toasts), next-themes, cmdk
Auth Better Auth (MongoDB adapter, Next.js cookies plugin, anonymous plugin for demo mode)
Database MongoDB via Mongoose 9 (cached connection)
Background Jobs Inngest (event-driven functions + AI inference step)
Forms react-hook-form, react-select-country-list
Email Nodemailer (Gmail transport)
Market Data Finnhub API (US symbol search & company profiles), TradingView Widgets (charts), Colombo Stock Exchange API (Sri Lanka indices, securities & trades)
Tooling ESLint 9 (eslint-config-next), Turbopack
Hosting Vercel

πŸ—οΈ Architecture

flowchart TD
    User([User / Browser])

    subgraph Next["Next.js 16 App (Vercel)"]
        Auth["(auth) routes\nsign-in / sign-up"]
        Root["(root) routes\ndashboard + /stocks/[symbol]"]
        Cse["(root)/cse routes\nSri Lanka overview + /cse/[symbol]"]
        Search["SearchCommand (⌘K)"]
        Actions["Server Actions\nauth / finnhub / watchlist"]
        CseActions["cse.actions\nunstable_cache 60s / 1h"]
        InngestRoute["/api/inngest route"]
    end

    subgraph External["External Services"]
        Mongo[("MongoDB Atlas")]
        BetterAuth["Better Auth"]
        Finnhub["Finnhub API"]
        CseApi["Colombo Stock Exchange\ncse.lk JSON API"]
        TradingView["TradingView Widgets"]
        Inngest["Inngest Cloud"]
        Anthropic["Anthropic (Claude)"]
        Gmail["Gmail / SMTP"]
    end

    User --> Auth
    User --> Root
    User --> Cse
    User --> Search
    Root --> TradingView
    Cse --> CseActions --> CseApi
    Search --> Actions --> Finnhub
    Search -- "merged results" --> CseActions
    Auth --> Actions
    Actions --> BetterAuth --> Mongo
    Actions -- "CSE watchlist rows" --> CseActions
    Actions -- "app/user.created" --> Inngest
    Inngest --> InngestRoute
    InngestRoute --> Anthropic
    InngestRoute --> Gmail
Loading

Key flows

  1. Sign-up - signUpWithEmail server action creates the user through Better Auth (stored in MongoDB), then emits an app/user.created Inngest event with the user's profile.
  2. Welcome email - The sendSignUpEmail Inngest function receives the event, calls Claude to generate a personalized intro paragraph, then sends the welcome email via Nodemailer.
  3. Search - The SearchCommand dialog debounces input and calls the searchStocks server action, which fans out to Finnhub (symbol search, or popular-symbol profiles when the query is empty) and the CSE feed in parallel, normalizes both to a common shape, and interleaves them so Sri Lankan matches are never buried under US ones. Either side failing degrades to the other.
  4. Charts - Dashboard and stock-detail pages render TradingView embed widgets client-side via the TradingViewWidget component and useTradingViewWidget hook.
  5. Colombo Stock Exchange - /cse renders on the server: cse.actions POSTs form-encoded requests to the cse.lk JSON API, wrapped in unstable_cache (60s for live data, 1h for reference data) and tagged cse. Every call falls back to an empty result on failure, so one dead endpoint degrades a single card instead of blanking the page. /cse/[symbol] reads the same cached feeds for company info, price ranges and detailed trades - no API key required.
  6. Cross-market watchlist - Watchlist rows carry a market flag (US | CSE). Hydration prices US rows from Finnhub and resolves every CSE row from one market-wide tradeSummary call, so a mixed watchlist costs a single extra request no matter how many Sri Lankan holdings it has.
  7. Demo mode - startDemoSession calls signInAnonymous; the hourly cleanup-demo-users Inngest cron deletes anonymous users (and their sessions/accounts) older than 24 hours.

πŸ“ Project Structure

my-app/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ (auth)/                 # Unauthenticated area (redirects to / if logged in)
β”‚   β”‚   β”œβ”€β”€ sign-in/page.tsx
β”‚   β”‚   β”œβ”€β”€ sign-up/page.tsx
β”‚   β”‚   └── layout.tsx
β”‚   β”œβ”€β”€ (root)/                 # Authenticated area (redirects to /sign-in if logged out)
β”‚   β”‚   β”œβ”€β”€ page.tsx            # Market dashboard
β”‚   β”‚   β”œβ”€β”€ stocks/[symbol]/    # Stock details
β”‚   β”‚   β”œβ”€β”€ watchlist/          # Saved companies
β”‚   β”‚   β”œβ”€β”€ cse/                # Colombo Stock Exchange overview
β”‚   β”‚   β”œβ”€β”€ cse/[symbol]/       # CSE security details
β”‚   β”‚   └── layout.tsx
β”‚   β”œβ”€β”€ api/inngest/route.ts    # Inngest serve endpoint (GET/POST/PUT)
β”‚   β”œβ”€β”€ layout.tsx              # Root layout (fonts, Toaster, theme provider)
β”‚   β”œβ”€β”€ icon.svg                # Favicon (compass rose, light + dark)
β”‚   β”œβ”€β”€ apple-icon.png          # Apple touch icon
β”‚   └── globals.css             # Design tokens + component layer
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ ui/                     # shadcn/ui + Base UI primitives
β”‚   β”œβ”€β”€ forms/                  # Reusable form fields (InputField, SelectField, …)
β”‚   β”œβ”€β”€ chart/                  # Background field, threshold rail, shared marks
β”‚   β”œβ”€β”€ cse/                    # Colombo Stock Exchange tables and cards
β”‚   β”œβ”€β”€ Header.tsx  NavItems.tsx  UserDropdown.tsx  NightLight.tsx
β”‚   β”œβ”€β”€ SearchCommand.tsx  TradingViewWidget.tsx  WatchlistButton.tsx  StockLogo.tsx
β”‚   β”œβ”€β”€ WatchlistTable.tsx  NoticeToInvestors.tsx  SessionCode.tsx
β”œβ”€β”€ database/
β”‚   └── mongoose.ts             # Cached Mongoose connection helper
β”œβ”€β”€ hooks/
β”‚   β”œβ”€β”€ useDebounce.ts
β”‚   └── useTradingViewWidget.tsx
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ actions/                # "use server" actions (auth, finnhub, watchlist, cse)
β”‚   β”œβ”€β”€ auth/auth.ts            # Better Auth instance (lazy, cached)
β”‚   β”œβ”€β”€ inngest/                # client, functions, prompts
β”‚   β”œβ”€β”€ nodemailer/             # transporter, templates
β”‚   β”œβ”€β”€ constants.tsx           # Nav items, form options, TradingView widget configs
β”‚   └── utils.ts                # cn() class-name helper
β”œβ”€β”€ types/
β”‚   └── global.d.ts            # Global type declarations
β”œβ”€β”€ public/                     # Static assets (logo, images)
β”œβ”€β”€ DESIGN.md                   # Design system: tokens, type ramp, component rules
β”œβ”€β”€ PRODUCT.md                  # Product context: users, purpose, constraints
β”œβ”€β”€ eslint.config.mjs
β”œβ”€β”€ next.config.ts
└── tsconfig.json

πŸš€ Getting Started

Prerequisites

  • Node.js 20+ and npm
  • A MongoDB database (a free MongoDB Atlas cluster works)
  • A Finnhub API key - free tier at finnhub.io
  • No key needed for the Sri Lanka section - the cse.lk API is public and unauthenticated
  • A Gmail account with an App Password (for sending email)
  • (Optional for local background jobs) the Inngest Dev Server

Installation

git clone https://github.com/Thinal-Fernando/StockLens.git
cd StockLens          # repository root is the Next.js app
npm install

Environment Variables

Create a .env.local file in the project root:

# App
NODE_ENV=development
NEXT_PUBLIC_BASE_URL=http://localhost:3000

# MongoDB
MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>/<database>?retryWrites=true&w=majority

# Better Auth
BETTER_AUTH_SECRET=<generate-a-long-random-secret>
BETTER_AUTH_URL=http://localhost:3000

# Market data
FINNHUB_API_KEY=<your-finnhub-api-key>

# Inngest / AI workflow
INNGEST_DEV=1
ANTHROPIC_API_KEY=<your-anthropic-api-key>

# Email (Gmail SMTP)
NODEMAILER_EMAIL=<your-gmail-address>
NODEMAILER_PASSWORD=<your-gmail-app-password>

Note: .env* files are gitignored. Never commit real credentials. In production, set these in your Vercel project's Environment Variables settings.

Running Locally

npm run dev

Open http://localhost:3000.

Running Background Jobs

Inngest functions (e.g. the personalized welcome email) run through the /api/inngest route. To exercise them locally, start the Inngest Dev Server alongside npm run dev:

npx inngest-cli@latest dev

It auto-discovers the app at http://localhost:3000/api/inngest. Open the Inngest dev dashboard (usually http://localhost:8288) to inspect events and function runs.

Registered functions:

Function Trigger Purpose
sendSignUpEmail app/user.created event Generate and send the AI-personalized welcome email.
cleanup-demo-users Cron 0 * * * * Delete anonymous demo accounts older than 24 hours.

πŸ“œ Available Scripts

Script Description
npm run dev Start the development server (Turbopack) on port 3000.
npm run build Create an optimized production build.
npm run start Serve the production build.
npm run lint Run ESLint across the project.

☁️ Deployment

The app is designed for Vercel:

  1. Push the repository to GitHub.
  2. Import the project into Vercel - the framework preset (Next.js) is detected automatically.
  3. Add every variable from Environment Variables under Project β†’ Settings β†’ Environment Variables (set BETTER_AUTH_URL to your production domain).
  4. Deploy. Vercel builds every push to main as a production deployment and every pull request as a preview deployment.

Because the frontend and the Inngest /api/inngest route are deployed together, connect the project to Inngest Cloud so background functions run in production.


πŸ” CI/CD

A GitHub Actions workflow runs on every push and pull request to main:

  • Lint - npm run lint
  • Type-check - tsc --noEmit
  • Build - npm run build

Deployments are handled by Vercel's Git integration. Branch protection on main requires the CI checks (and the Vercel preview build) to pass before a pull request can be merged, so production is only ever built from vetted commits.


🀝 Contributing

Contributions are welcome.

  1. Fork the repository and create a feature branch: git checkout -b feature/your-feature.
  2. Make your changes and ensure npm run lint and npm run build pass.
  3. Commit using clear, conventional messages (e.g. feat(search): …, fix(auth): …).
  4. Open a pull request against main with a description of the change.

πŸ“„ License

Released under the MIT License. Copyright (c) 2026 Thinal Fernando.

About

StockLens is a full-stack stock market dashboard built with Next.js, featuring real-time market insights, stock search and analysis, secure authentication, and personalized automated onboarding.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages