Skip to content
lenzencPublic

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Unified — Meridian Advisor Console

A full-stack wealth management platform for Registered Investment Advisors (RIAs). Advisors log in to a firm-scoped console that surfaces their practice structure, household relationships, client portfolios, and planning data.

Stack

Layer Technology
API FastAPI + SQLModel (SQLAlchemy 2 async) + asyncpg
Database PostgreSQL (Docker Compose) + Alembic migrations
Auth JWT (HS256, 12h) — separate advisor and client flows
UI Next.js 16 (App Router) + React 19 + Tailwind CSS v4
Package mgmt uv (Python) · pnpm (Node)

Quick start

Prerequisites

  • Docker Desktop (for Postgres)
  • uv — Python toolchain
  • pnpm — Node package manager
  • Node.js 20+

1 — Clone and configure

cp .env.example .env

The defaults in .env.example work for local development out of the box.

2 — Start Postgres

docker compose up -d

Postgres listens on localhost:5432. Data persists in a named Docker volume.

3 — Install Python dependencies

uv sync

4 — Run migrations and seed data

make migrate   # applies all Alembic migrations
make seed      # inserts Summit Wealth Advisors + 5 sample households

The seed creates Summit Wealth Advisors (RIA, Denver CO) with two practices, six advisors, and five realistic retirement-planning households. All seed accounts use the password password.

5 — Start the API

make run       # FastAPI on http://localhost:8000

Interactive API docs: http://localhost:8000/docs

6 — Start the UI

make ui-install   # install Node dependencies (first time only)
make ui-dev       # Next.js dev server on http://localhost:3000

7 — Log in

Visit http://localhost:3000/login and sign in with any seeded advisor account, for example:

Email Password
jcarter@summitwealthadvisors.example.com password
lpark@summitwealthadvisors.example.com password
rchen@summitwealthadvisors.example.com password

After login you land on the Firm Structure dashboard — firm overview, KPI cards, and a browsable grid of practices and advisors.

Common commands

# Full reset — wipe tables, re-migrate, re-seed
make reset && make migrate && make seed

# Generate a new migration after editing a model
uv run alembic revision --autogenerate -m "describe the change"

# Roll back one migration
uv run alembic downgrade -1

# Add a Python dependency
uv add <package>

# Add a UI dependency
cd ui && pnpm add <package>

Project layout

app/              FastAPI application
  main.py         App factory + router registration
  config.py       Settings (reads .env)
  database.py     Async engine + session dependency
  security.py     JWT + bcrypt
  authz.py        Firm-scoped authorization (Scope dataclass)
  models/         SQLModel table models + Pydantic schemas
  crud/           Generic CRUDBase + per-entity instances
  routers/        One APIRouter per entity

alembic/          Database migrations
  versions/       Hand-written raw-SQL migration files

scripts/
  seed.py         Dev seed — 2 firms, 2 practices, 7 advisors, 7 households

ui/               Next.js 16 frontend (App Router)
  app/            Pages and Server Actions
    login/        Advisor login
    dashboard/    Firm Structure landing (post-login)
    portal/       Client portal (post-login)
  components/
    app-shell/    Sidebar + authenticated layout
    firm-structure/ Firm overview page components
    auth/         Login forms
    landing/      Public marketing page
  lib/
    format.ts     AUM formatting, initials, token helpers
    types.ts      Shared TypeScript interfaces

design/           Design system
  tokens.css      CSS custom properties (single source of truth)
  tailwind.config.js  Tailwind v3 config (mirrors tokens.css)
  references/     Drop screenshot mockups here for design sessions

Architecture notes

  • Auth — cookie-session; the advisor JWT is stored in an httpOnly cookie (advisor_session). The Next.js proxy (ui/proxy.ts) does a fast JWT-payload check before rendering protected routes; the page itself re-validates against GET /api/v1/auth/me.
  • Authorization — every API request is scoped to a firm_id. Advisors only see households they are assigned to via advisor_assignment.
  • Enum fields — all enum-typed SQLModel columns use sa_column=Column(String(N)) to avoid PostgreSQL native enum type conflicts with VARCHAR+CHECK columns in the schema.
  • Migrations — hand-written raw SQL (op.execute()) to preserve CHECK constraints, GIN/partial indexes, and DDL comments. Never autogenerate for production.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages