Skip to content

Latest commit

 

History

27 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Finora v2 💸

Finora is a personal expense tracker built as a modern full-stack TypeScript application. It lets you log daily expenses, browse them by day / month / year, and visualise your monthly spending — all with a clean, responsive UI.

Live Preview: https://finora-two.vercel.app/

This is the v2 rewrite of the original Finora codebase, modernised to match today's state-of-the-art stack while preserving the original coding and commenting patterns.


✨ Features

  • 🔐 Email/password authentication with bcrypt hashing + JWT stored in an httpOnly cookie
  • 🌐 Google OAuth 2.0 login via Passport (optional)
  • 📅 Add, edit & delete expenses (inline editing on desktop, popover editing on mobile)
  • 🗓️ Day / Month / Year summary views with running totals
  • 📊 Recharts bar chart of yearly spending
  • 🎨 Themed UI (Tailwind v4) with the original Finora palette (forest-mint, soft-teal, matcha…)
  • ⚡ Server state handled by TanStack Query (auto-refetch after every mutation)
  • 🛡️ Zod validation on both client and server

🧱 Tech Stack

Layer Technology
Frontend React 19, TypeScript, Vite 8, Tailwind CSS 4, React Router 7
Data fetching TanStack Query, Axios
Forms React Hook Form + Zod
State Zustand (client state)
Charts Recharts
Backend Express 5, TypeScript, tsx
Database MongoDB + Mongoose 9
Auth JWT (httpOnly cookie), Passport Google OAuth 2.0
Security Helmet, httpOnly cookies, CORS, environment-validated config

📁 Repository Structure

Finora/
├── finora/          # Frontend (Vite + React)
│   └── src/
│       ├── api/         # Axios instance
│       ├── components/  # Feature + UI components
│       ├── hooks/       # TanStack Query hooks (queries & mutations)
│       ├── lib/         # Utilities
│       ├── pages/       # Route pages
│       ├── schema/      # Zod schemas (client)
│       ├── services/    # API service functions
│       ├── stores/      # Zustand stores
│       └── types/       # Shared TypeScript types
└── finora-api/       # Backend (Express REST API)
    └── src/
        ├── auth/        # Passport Google OAuth strategy
        ├── config/      # env validation
        ├── controllers/ # Route handlers
        ├── db/          # MongoDB connection
        ├── middlewares/ # auth, validation, error handling
        ├── models/      # Mongoose schemas
        ├── routes/      # Express routers
        ├── schema/      # Zod schemas (server)
        ├── types/       # Global type augmentations
        └── utils/       # asyncHandler, token helpers

🚀 Getting Started

Prerequisites

  • Node.js >= 20.19 (Vite 8 requirement) or 22.12+
  • MongoDB running locally (or an Atlas cluster)

1. Backend (finora-api)

cd finora-api
cp .env.example .env.local
# edit .env.local with your values
npm install
npm run dev        # starts API on http://localhost:3000

Minimum env vars: JWT_SECRET, SESSION_SECRET, LOCAL_MONGODB_URI. Google OAuth vars are optional (required only for "Login with Google").

2. Frontend (finora)

cd finora
cp .env.example .env
npm install
npm run dev        # starts Vite dev server on http://localhost:5173

Open http://localhost:5173 and you're good to go.


📜 Scripts

Frontend

Script Description
npm run dev Start Vite dev server
npm run build Type-check + production build
npm run lint ESLint check
npm run typecheck TypeScript check only
npm run preview Preview the production build

Backend

Script Description
npm run dev Start API with tsx watch
npm run build Compile TypeScript to dist/
npm start Run the compiled server
npm run typecheck TypeScript check only

🔌 API Overview

Base URL (dev): http://localhost:3000

Method Endpoint Auth Description
POST /user/register Register + auto login
POST /user/login Login
DELETE /user/logout Logout (clears cookie)
GET /user/profile Current user profile
PATCH /user/update Update profile (password required)
GET /user/authenticate/me Restore session
GET /user/:id Get user by id
POST /expense/create Create expense
PATCH /expense/update/:id Update expense (owner only)
DELETE /expense/delete/:id Delete expense (owner only)
GET /expense/by-date Expenses for a date
GET /expense/monthly Summary: day → total
GET /expense/yearly Summary: month → total
GET /auth/google Google OAuth start
GET /auth/google/callback Google OAuth callback

All authenticated routes read the JWT from the token httpOnly cookie.


🔐 Auth Flow

  1. User registers / logs in → server signs a JWT (1-day expiry) and sets it as an httpOnly cookie (Secure + SameSite=None in production, Lax in development). Registration auto-logs-in.
  2. Frontend calls /user/authenticate/me on boot to restore the session into the Zustand auth store.
  3. Protected routes (/dashboard, /user/:id, /update-info) redirect to /login when unauthenticated.
  4. Logging out clears the cookie and resets the TanStack Query cache.

🆚 What changed in v2

The goal of v2 was: update to state-of-the-art tech, fix broken UI/logic/code, preserve the original coding & commenting patterns.

Stack upgrades

  • Vite 6 → 8, React 19.2, TypeScript 5.9, Tailwind 4.3
  • React Router DOM 7 (kept createBrowserRouter)
  • Added TanStack Query v5 for all server state
  • Zustand now only holds pure client state (auth session + selected date)
  • Zod 3 → 4, date-fns 2 → 4, react-day-picker 8 → 10, Recharts 2.15
  • Backend: Express 5.2, Mongoose 8 → 9, bcrypt → bcryptjs, ts-node/nodemon → tsx
  • Added Helmet, compression, centralized error handler, and zod server-side validation

Bugs fixed

  • Frontend: lists did not refresh after create/update/delete → mutations now invalidate queries
  • Frontend: avatar fallback never rendered for users without a profile image
  • Frontend: untracked mobile edit popover never prefilled / closed → now prefills and closes on submit
  • Frontend: DashboardChart used hardcoded data → now shows real yearly spending
  • Backend: secure: true cookie always set (broke local http dev) → only in production
  • Backend: name.trim() === null validation bug → proper zod validation
  • Backend: getMonthlyExpences typo → getMonthlyExpenses
  • Backend: missing error-handler middleware → centralized error handler + 404 handler
  • Backend: @/ path aliases broke the compiled node dist/index.js start → relative imports
  • Backend: Google OAuth strategy imported ..//models/user and referenced a missing googleId field → fixed
  • Backend: session serialization stored the whole user object → stores only the id
  • Backend: Google OAuth credentials were required at boot even when unused → strategy now optional

☁️ Deployment Notes

  • Build the frontend with npm run build → output in dist/ (serve statically, e.g. nginx / Netlify / Vercel).
  • Set NODE_ENV=production, VITE_MODE=production, VITE_API_URL, MONGODB_URI, JWT_SECRET, SESSION_SECRET, CLIENT_URL.
  • Production cookies use Secure + SameSite=None; the API must run behind HTTPS.
  • API is a standard Node server: npm run build && npm start.

📄 License

This project is licensed under the MIT License.

About

Finora is a personal expense tracker built as a modern full-stack TypeScript application.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages