{"id":31390862,"url":"https://github.com/evanmarshall-dev/ledger-light","last_synced_at":"2026-04-11T12:02:54.564Z","repository":{"id":314977361,"uuid":"1057587909","full_name":"evanmarshall-dev/ledger-light","owner":"evanmarshall-dev","description":"A simple, secure, session‑based personal finance tracker built with the MEN stack (MongoDB, Express, Node) and EJS templates. Track income and expenses, filter by month or category, and manage your transactions with full CRUD. Only you can see and modify your own data.","archived":false,"fork":false,"pushed_at":"2025-09-23T03:57:59.000Z","size":7594,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-23T05:48:27.298Z","etag":null,"topics":["application","crud","ejs","express","mongodb","mongoose","nodejs"],"latest_commit_sha":null,"homepage":"https://ledger-light-3d68c6f45346.herokuapp.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evanmarshall-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-16T00:04:45.000Z","updated_at":"2025-09-23T03:58:02.000Z","dependencies_parsed_at":"2025-09-16T02:28:52.131Z","dependency_job_id":"a80da832-0868-4762-8877-93afd86a9610","html_url":"https://github.com/evanmarshall-dev/ledger-light","commit_stats":null,"previous_names":["evanmarshall-dev/ledger-light"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/evanmarshall-dev/ledger-light","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmarshall-dev%2Fledger-light","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmarshall-dev%2Fledger-light/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmarshall-dev%2Fledger-light/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmarshall-dev%2Fledger-light/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evanmarshall-dev","download_url":"https://codeload.github.com/evanmarshall-dev/ledger-light/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmarshall-dev%2Fledger-light/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":277455214,"owners_count":25820678,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-28T02:00:08.834Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application","crud","ejs","express","mongodb","mongoose","nodejs"],"created_at":"2025-09-29T01:37:46.502Z","updated_at":"2025-09-29T01:37:47.543Z","avatar_url":"https://github.com/evanmarshall-dev.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LedgerLight\n\n![Language: JavaScript](https://img.shields.io/badge/language-JavaScript-F7DF1E?logo=javascript\u0026logoColor=black)\n![Runtime: Node 18+](https://img.shields.io/badge/node-18%2B-339933?logo=node.js\u0026logoColor=white)\n![Express 5](https://img.shields.io/badge/express-5.x-000000?logo=express)\n![EJS 3](https://img.shields.io/badge/ejs-3.x-8a2be2)\n![Mongoose 8](https://img.shields.io/badge/mongoose-8.x-47A248?logo=mongodb\u0026logoColor=white)\n![License: ISC](https://img.shields.io/badge/license-ISC-blue)\n\n## Live Demo\n\n[![Live Demo](https://img.shields.io/badge/Live-Demo-brightgreen?style=for-the-badge)](https://ledger-light-3d68c6f45346.herokuapp.com/)\n\nA simple, secure, session‑based personal finance tracker built with the MEN stack (MongoDB, Express, Node) and EJS templates. Track income and expenses, filter by month or category, and manage your transactions with full CRUD—only you can see and modify your own data.\n\n![LedgerLight app screenshot](./public/images/app-screenshot.png \"LedgerLight transactions UI with filters and actions\")\n\n## Overview\n\nLedgerLight was built to practice full‑stack fundamentals: secure authentication, authorization, RESTful routing, server‑rendered views, and a clean, accessible UI. It implements per‑user data ownership, CSRF protection, a strict Content Security Policy, and Mongo‑backed sessions so you can deploy with confidence.\n\n### Core features\n\n- Account registration and login with bcrypt password hashing\n- Session‑based auth with MongoDB session store; logout support\n- Transactions: create, read, update, delete (CRUD)\n- Per‑user authorization: only the owner can view/edit/delete their data\n- Filters and sorting on transactions list:\n  - Filter by month (YYYY‑MM) and category\n  - Sort by date or title, ascending/descending\n- CSRF protection and Helmet security headers (including a restrictive CSP)\n- Method override to support PUT/DELETE from HTML forms (`?_method=PUT`)\n- Flash messages and clean, accessible server‑rendered EJS views\n\n## Getting started\n\n### Deployed app and planning materials\n\n- Deployed app: [https://ledger-light-3d68c6f45346.herokuapp.com](https://ledger-light-3d68c6f45346.herokuapp.com)\n- Trello (planning): [https://trello.com/b/zVqBrZsS/ledger-light](https://trello.com/b/zVqBrZsS/ledger-light)\n\nIf you share those links, we’ll update them here.\n\n### Prerequisites\n\n- Node.js 18+ (LTS recommended)\n- pnpm (project uses a `pnpm-lock.yaml`)\n- A MongoDB instance/connection string\n\n### Environment configuration\n\nCreate a `.env` file at the project root with:\n\n```ini\nMONGO_URI=mongodb+srv://\u003cuser\u003e:\u003cpassword\u003e@\u003ccluster\u003e/\u003cdb\u003e?retryWrites=true\u0026w=majority\nSESSION_SECRET=\u003ca-long-random-string\u003e\nPORT=1986            # optional, defaults to 1986\nNODE_ENV=development # optional\n```\n\nRequired vars are validated on boot. If any are missing, the server will exit with a helpful error.\n\n### Install and run\n\n```sh\npnpm install\npnpm dev\n```\n\nThen open [http://localhost:1986](http://localhost:1986).\n\nAvailable scripts:\n\n- `pnpm dev` – start in watch mode with nodemon\n- `pnpm start` – start the server (production‑style)\n\n### Quick tour\n\n- Home: `/`\n- Register: `/auth/register`\n- Sign in: `/auth/login`\n- Transactions index: `/transactions`\n  - Query params: `?month=YYYY-MM\u0026category=Food\u0026sort=date_desc`\n\nForms that change state include a CSRF token; when creating your own forms, include:\n\n```ejs\n\u003c% if (csrfToken) { %\u003e\n  \u003cinput type=\"hidden\" name=\"_csrf\" value=\"\u003c%= csrfToken %\u003e\"\u003e\n\u003c% } %\u003e\n```\n\nHTML forms can use PUT/DELETE via the `method-override` query param:\n\n```html\n\u003cform action=\"/transactions/\u003c%= tx._id %\u003e?_method=DELETE\" method=\"post\"\u003e\n  \u003cbutton type=\"submit\"\u003eDelete\u003c/button\u003e\n  \u003c!-- include the CSRF hidden input as above --\u003e\n\u003c/form\u003e\n```\n\n## Data model\n\nUser\n\n- `email` (unique, required)\n- `username` (unique, required, lowercase)\n- `passwordHash` (bcrypt)\n\nTransaction\n\n- `title` (required)\n- `amount` (Number, required)\n- `date` (Date, defaults to now)\n- `category` (String)\n- `notes` (String)\n- `owner` (ObjectId → User, required)\n\n## Technologies used\n\n- Runtime: Node.js, pnpm\n- Web framework: Express 5\n- Views: EJS templating\n- Database/ODM: MongoDB + Mongoose\n- Sessions: express‑session + connect‑mongo\n- Auth: bcrypt for password hashing\n- Security: Helmet (CSP, headers), custom CSRF middleware, SameSite=strict cookies\n- Utilities: method‑override, morgan\n- Styling: custom CSS with modern variables and accessible focus styles\n\n## Attributions\n\n- Typeface: Inter by Rasmus Andersson — served via Google Fonts, licensed under the SIL Open Font License. [https://fonts.google.com/specimen/Inter](https://fonts.google.com/specimen/Inter)\n\nIf you add third‑party icons, images, or other assets that require attribution, list them here with links to the source and license.\n\n## Configuration notes\n\n- Default port is `1986`. Override with `PORT`.\n- The CSP is intentionally strict. If you load external assets (e.g., fonts, analytics), update the Helmet `contentSecurityPolicy` directives in `server.js` accordingly (e.g., add `fontSrc: [\"'self'\", \"https:\", \"data:\"]`).\n- Sessions are stored in MongoDB and cookies are `HttpOnly` and `SameSite=strict`. In production, cookies are marked `Secure`.\n\n## Next steps (stretch goals)\n\n- Budgets and category summaries; charts (e.g., Chart.js) for monthly trends\n- Export/import (CSV), and bulk actions\n- Recurring transactions and reminders\n- Pagination and full‑text search\n- Tags in addition to categories; custom category management\n- Account types (cash, bank, credit) and multi‑currency support\n- Password reset flow, email verification, optional 2FA\n- Rate limiting and login throttling; audit logging\n- API endpoints (JSON) and a small SPA client\n- Dockerfile and one‑click deployment scripts\n\n## Project structure\n\n```text\n.\n├── .env\n├── .gitignore\n├── package.json\n├── pnpm-lock.yaml\n├── pnpm-workspace.yaml\n├── README.md\n├── server.js\n├── config/\n│   └── index.js\n├── controllers/\n│   ├── authController.js\n│   └── transactionsController.js\n├── middleware/\n│   ├── auth.js\n│   ├── csrf.js\n│   └── flash.js\n├── models/\n│   ├── transaction.js\n│   └── user.js\n├── public/\n│   ├── images/\n│   │   ├── app-screenshot.png\n│   │   └── example-trello.png\n│   ├── js/\n│   │   └── main.js\n│   └── styles/\n│       ├── auth.css\n│       ├── globals.css\n│       ├── partials.css\n│       └── transactions.css\n├── routes/\n│   ├── auth.js\n│   ├── index.js\n│   └── transactions.js\n└── views/\n    ├── index.ejs\n    ├── auth/\n    │   ├── login.ejs\n    │   └── register.ejs\n    ├── partials/\n    │   ├── _flash.ejs\n    │   ├── _head.ejs\n    │   └── _navbar.ejs\n    └── transactions/\n        ├── edit.ejs\n        ├── index.ejs\n        ├── new.ejs\n        └── show.ejs\n```\n\n## RESTful routing\n\nThis section documents the primary RESTful routes used by LedgerLight. The app follows conventional REST patterns for authentication and transaction resources. Below is a concise table of routes, the HTTP methods they accept, and their purpose.\n\n| Route                    |        Method(s) | Purpose                                                  |\n| ------------------------ | ---------------: | -------------------------------------------------------- |\n| `/`                      |              GET | Home / dashboard (transactions summary)                  |\n| `/auth/register`         |        GET, POST | Show registration form / create new user                 |\n| `/auth/login`            |        GET, POST | Show login form / create session                         |\n| `/auth/logout`           |             POST | Destroy session (logout)                                 |\n| `/transactions`          |        GET, POST | List user's transactions / create a new transaction      |\n| `/transactions/new`      |              GET | Form to create a new transaction                         |\n| `/transactions/:id`      | GET, PUT, DELETE | Show a single transaction / update / delete (owner-only) |\n| `/transactions/:id/edit` |              GET | Form to edit an existing transaction                     |\n\nNotes:\n\n- HTML forms that perform PUT/DELETE use method-override via the `?_method=` query param (e.g. `POST /transactions/:id?_method=PUT`).\n- All state-changing routes require a CSRF token and an authenticated session.\n\n## Deployment (Heroku)\n\nBelow is a minimal, production‑safe setup for deploying to Heroku using the official Node.js buildpack.\n\n1. Create the app and set config vars\n\n```sh\n# create an app (or use the Heroku Dashboard GitHub integration)\nheroku create ledger-light\n\n# required configuration\nheroku config:set MONGO_URI=\"\u003cyour-mongodb-uri\u003e\"\nheroku config:set SESSION_SECRET=\"\u003ca-long-random-string\u003e\"\nheroku config:set NODE_ENV=production\n```\n\n1. Ensure the web process starts your server\n\nHeroku runs `pnpm start` by default. This project’s start script runs `node server.js`, which is compatible.\n\nOptional: add a Procfile if you prefer explicitness:\n\n```procfile\nweb: node server.js\n```\n\n1. Trust proxy for secure cookies (recommended)\n\nHeroku terminates TLS at the router. To allow `secure` cookies to be set correctly behind the proxy, enable trust proxy in production (place before session middleware):\n\n```js\n// server.js\nif (process.env.NODE_ENV === \"production\") {\n  ledgerApp.set(\"trust proxy\", 1);\n}\n```\n\n1. Deploy\n\n```sh\n# if using git push workflow\ngit push heroku main\n\n# then open the app\nheroku open\n```\n\nIf you run into CSP issues after adding third‑party assets, update the Helmet `contentSecurityPolicy` directives in `server.js` (e.g., allow external font or script hosts).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevanmarshall-dev%2Fledger-light","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevanmarshall-dev%2Fledger-light","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevanmarshall-dev%2Fledger-light/lists"}