Commit Graph

50 Commits

Author SHA1 Message Date
steve acf8f9f4da fix: replace inline onclick handlers blocked by CSP with event delegation
The Content-Security-Policy header (default-src 'self') blocked inline
onclick attributes, silently preventing the Edit and Delete buttons in
the user management modal from firing. Replaced with data-id attributes
and a delegated click listener on the users-list container.
2026-03-31 10:49:23 -06:00
steve 3b1f196f67 chore: update package-lock.json for nodemailer 2026-03-31 10:24:57 -06:00
steve fc114d0ec6 feat: add password reset, SMTP settings, and Add Account button
Password reset: users with a registered email can request a reset link
from the login screen. A one-hour signed token is emailed via SMTP;
clicking the link opens a set-new-password form. Tokens are hashed
(SHA-256) before storage and invalidated after use.

SMTP settings: admin-only panel in the Users modal lets admins
configure host, port, encryption, credentials, and from address.
Settings persisted in a new key-value settings table. The SMTP
password is never returned to the client.

Users: email field added to the create/edit form and stored in a new
users.email column. Email is used for password reset lookup.

Add Account: admins now have a + button in the header that opens the
existing setup wizard to add additional checking accounts.

Schema: adds password_reset_tokens and settings tables with automatic
runtime migrations for existing databases.
2026-03-31 10:21:49 -06:00
steve 444e24a191 Fix remaining critical, medium, and low security issues
IDOR (critical): GET /api/checks/:id and GET /api/deposits/:id now
verify the requesting user has access to the record's account before
returning data. Previously any authenticated user could fetch any
record by ID across accounts.

Printed check guard (critical): PUT and DELETE on checks now return
409 if the check has already been printed, enforcing the business rule
that printed checks are immutable. Previously the printed flag was
only enforced in the frontend.

PDF DoS (medium): checkIds array capped at 300 (100 pages × 3 per page).

QBO import DoS (medium): records array capped at 1000 per confirm call.

PDF error detail (medium): internal err.message no longer returned to
the client on PDF generation failure.

SESSION_SECRET (low): removed NODE_ENV=production condition — the
server now exits immediately on startup if SESSION_SECRET is unset
regardless of environment. Dev script updated to load .env via
node --env-file=.env so developers set it once in a local .env file.

Password hints (low): updated all three UI labels from "min 8 chars"
to "min 10 chars, include a digit or symbol" to match the actual
server-side validation.
2026-03-20 13:28:18 -06:00
steve bd3e66cd44 Fix low-level security issues
Content-Security-Policy: add header with default-src 'self',
unsafe-inline for styles (needed for JS-generated inline style attrs),
and data: for embedded logo/signature images.

JSON body limit: reduce from 10mb to 2mb (logo cap is 512KB base64).

Session maxAge: now configurable via SESSION_MAX_AGE_HOURS env var
(default 168h / 7 days). Documented in .env.example.

Password strength: centralize validation in auth.js and raise the bar
to 10+ characters with at least one letter and one non-letter. Applied
consistently to all four password-setting paths (initial setup,
login change-password, admin create user, admin edit user).
2026-03-20 12:25:42 -06:00
steve 2939bfa608 Fix high and medium security vulnerabilities
CSRF: upgrade session cookie sameSite from 'lax' to 'strict'.

Rate limiting: login endpoint now blocks an IP after 10 failed attempts
in a 15-minute window; resets on success. In-memory, no new dependency.

SESSION_SECRET: server exits at startup when NODE_ENV=production and
SESSION_SECRET is unset. docker-compose.yml updated to pass it via env;
.env.example added with generation instructions.

Security headers: add X-Content-Type-Options, X-Frame-Options, and
Referrer-Policy to all responses.

Sensitive data: routing_number and account_number are now omitted from
GET /api/account/:id responses for non-admin users.

Image size: logo upload capped at 512 KB in the account PUT handler.

Amount validation: checks (POST/PUT) and deposit items (POST/PUT) now
reject non-finite and non-positive amounts.

QBO import: uploaded file is rejected if its MIME type is not text or
a known CSV variant.
2026-03-20 08:21:23 -06:00
steve 0f00624e61 Fix three critical authorization vulnerabilities
mark-printed: was only checking the first check's account — now fetches
all check IDs upfront, verifies they all exist and share the same
account, then checks editor access once on that account.

PDF generation: was authorizing against the client-supplied account_id
but fetching checks by ID without confirming they belong to that account
— now rejects any check ID whose account_id doesn't match.

Role/account-assignment changes: active sessions for the affected user
are now deleted immediately via json_extract on the sessions table, so
demotions take effect at once rather than at session expiry (up to 7d).
2026-03-19 22:44:11 -06:00
steve 4783cf8953 Restrict write-action UI to editor+ role for checks and deposits
Viewers can no longer see New Check, New Deposit, Import .mdb, or
Import QBO buttons. Deposit row Edit/Delete buttons are also hidden
for viewers, matching the existing behavior on check rows.
2026-03-19 16:32:03 -06:00
steve af88549ad8 Modal scroll fix; per-account editor/viewer roles
- Fix account settings modal overflow: add max-height to .modal, make
  .modal-body flex/scrollable, widen #acct-settings-modal to 620px
- Add role column to user_accounts (editor|viewer) with migration;
  existing assignments promoted to editor
- New isEditorForAccount() in auth middleware for per-account write checks
- Replace global requireEditor with per-account checks in checks.js,
  deposits.js, pdf.js, deposit-pdf.js, qbo-import.js
- GET /api/accounts now returns user_role per account
- users.js returns {account_id, role} per assignment; POST/PUT accept
  accounts as [{id, role}]
- Frontend: state.accountRole tracks effective role for active account;
  applyRoleUI and renderRow use it; user management shows role dropdown
  per account assignment
2026-03-18 23:31:23 -06:00
steve 764def4f7d Fix isEditor temporal dead zone error in renderRow 2026-03-18 23:02:08 -06:00
steve f827210a07 Implement user authentication and role-based access control
Three-tier user model: admin (all accounts, all actions), editor
(assigned accounts, read/write), viewer (assigned accounts, read-only).

Backend:
- express-session with custom SQLite session store (no extra packages)
- bcryptjs for password hashing
- src/middleware/auth.js: requireAuth, requireAdmin, requireEditor,
  canAccessAccount helpers
- src/routes/auth.js: login, logout, /me, setup-needed, change-password
- src/routes/users.js: full CRUD + account assignments (admin only)
- All API routes protected; /api/accounts filtered by user access;
  write routes gated by requireEditor; admin-only routes locked down

Frontend:
- Login overlay (full-page) with first-run admin-setup flow
- Role-based UI: admin-only elements hidden for non-admins; edit/delete
  and PDF buttons hidden for viewers; account switcher shows only
  accessible accounts for non-admins
- Users modal (admin only): user list with role badges, create/edit/delete
  users, set account access via checkboxes
- Change-password section available to all logged-in users
- apiFetch redirects to login on 401
2026-03-18 22:55:17 -06:00
steve 1277fc4aad Fix delete account: also remove layout_fields rows before deleting account 2026-03-18 21:37:34 -06:00
steve e4feafa82b Add Delete Account button with confirmation modal
Red "Delete Account" button at top of Account Settings. Clicking opens
a second modal with an irreversibility warning and the account name,
requiring a second explicit confirmation before deletion. Deletes the
account row plus all associated checks and deposits. Redirects to the
setup wizard if no accounts remain.
2026-03-18 21:15:05 -06:00
steve 35a5d576ea Add QuickBooks Online CSV importer for checks and deposits
Two-tab modal: "Checks to Print" parses QBO Transaction List / Check
Detail CSV exports; "Deposits" parses QBO Deposit Detail CSV exports
and groups by date. Both tabs show a preview before confirming import.
2026-03-18 14:55:21 -06:00
steve c944c84939 Thinner check lines; second signature line toggle
- Halve all check line thicknesses (line_thick * 0.5)
- Add second_signature column to account table with runtime migration
- Account settings toggle: enables a second signature line drawn 0.25"
  above the primary signature line, matching its width and thickness
2026-03-18 14:04:15 -06:00
steve 964823c8b4 Check 1 position fix; remove comma from next check number display
- Move check slot 0 up 0.25" to match physical stock alignment;
  slots 1 and 2 are unaffected
- Remove toLocaleString() from next check number header display
  so no comma appears in the thousands place
2026-03-18 12:36:23 -06:00
steve 2f2646fb78 Add one-off next check number override
Pencil button next to "Next check: X" in the header opens a modal
with a warning about sequence gaps/duplicates. Saves via new
PUT /api/account/:id/check-no endpoint (sets current_check_no = next - 1).
2026-03-18 10:56:05 -06:00
steve 830a7ca686 Deposit slip: remove outer border rect, switch deposit total to Courier
- Remove the outer border rect around the main content area
- Change DEPOSIT TOTAL font from Helvetica-Bold to Courier to match
  the monospaced style used for other amount fields
- Update strip element Y positions for better spacing
2026-03-13 10:33:19 -06:00
steve a97897c30a Deposit slip: landscape page; strip deposit total combined to one line 2026-03-13 10:19:36 -06:00
steve 4a56f15fc6 Deposit slip: portrait layout explicit; MICR repositioned below deposit total 2026-03-13 09:58:30 -06:00
steve ac5670039a Check select-all, filtered total, deposit slip layout fixes
- Add select-all checkbox to checks table header; checks/unchecks all
  visible (filtered) rows; supports indeterminate state
- Add summary bar below checks toolbar showing count and total amount
  of filtered checks
- Deposit slip: output to full 8.5x11 letter page for trimming
- Deposit slip: remove beige left strip fill (white background)
- Deposit slip: remove vertical separator between depositor/bank info
- Deposit slip: stack bank info below account info instead of side-by-side
- Deposit slip: lower date underline position
- Deposit slip left strip: flip text orientation to read tilt-left
  (rotate 90 instead of -90; reposition all strip element anchors)
- Deposit slip left strip: center MICR and labels in strip width
- Deposit slip total: include decimal point in rotated digit amount
2026-03-13 09:48:21 -06:00
steve 4fb7fd209c Add deposit slip and report generation
- New Deposits tab with ledger: date, checks total, cash, deposit total, item count, status
- Slide-in deposit panel: date, currency, coin, cash back, dynamic check entry rows, live totals
- Save deposit, then generate Deposit Slip or Deposit Report PDF
- Deposit slip: 3.375" x 8.5" portrait with Style A background drawn server-side,
  digit-column amounts, GnuMICR routing/account line rotated 90 deg, rotated
  deposit total and check count in left margin
- Deposit report: plain Courier ledger with depositor/bank info, check grid, totals
- deposits and deposit_items tables in schema; ON DELETE CASCADE for items
- Routes: GET/POST/PUT/DELETE /api/deposits, POST /api/deposit-pdf
- Generating a slip marks deposit as printed; date range and status filters
- README updated to describe deposit slip feature
2026-03-13 08:43:34 -06:00
steve a89db179cd Add account settings edit UI and GnuMICR.otf font
- Add gear button in header to open account settings modal
- Modal covers Organization, Bank, Account, Logo upload, and Printer Offset fields
- PUT /api/account/:id backend endpoint with full field validation
- Logo file reader with inline preview; only updates logo if a new file is chosen
- CSS for btn-header-icon, settings-section-label, logo-preview, form-row-4
- Add GnuMICR.otf (tracked alongside existing GnuMICR.ttf)
2026-03-12 22:58:56 -06:00
steve 4ec51c09fd Merge pull request #1 from snachodog/dependabot/npm_and_yarn/multer-2.1.1
Bump multer from 1.4.5-lts.2 to 2.1.1
2026-03-12 22:51:39 -06:00
steve c689ddc72a Fix startup crash on existing DB: move account_id indexes after migration 2026-03-12 22:24:15 -06:00
steve e81a4386d2 Add multi-account support
- Schema: account_id FK on checks and layout_fields; UNIQUE per-account on check_no and field_name
- DB: runtime migration recreates both tables to add account_id (assigns existing rows to account 1)
- Routes: GET /api/accounts lists all; GET /api/account/:id replaces hardcoded id=1; POST /api/account/setup always creates a new account and returns accountId
- checks.js: all queries scoped by account_id; POST requires account_id in body
- pdf.js: resolves account from check's account_id instead of id=1; layout fields fetched per-account
- import-mdb.js: always INSERTs a new account (never deletes existing); all records tagged with new accountId
- Frontend: account switcher in header; activeAccountId persisted to localStorage; all API calls pass account_id; switching accounts reloads checks; wizard and import auto-switch to newly created account
2026-03-12 22:13:52 -06:00
steve 5f9cc16ea5 Support multi-page PDFs for more than 3 selected checks
- pdfService: batch checks into groups of 3, add doc.addPage() between groups
- pdf route: remove 1-3 limit, accept any number of check IDs
- Frontend: remove 3-check selection cap; any number of checks can be selected
2026-03-12 21:16:37 -06:00
steve 667ec146cc Replace printed/unprinted tabs with unified list and inline filters
- All checks shown in one table by default (load all, no server-side printed filter)
- Add payee search, date-from/to, and status filter controls to toolbar
- Filtering is client-side; no extra API calls on filter changes
- All checks get Edit/Delete buttons regardless of printed status
- All checks get checkboxes for PDF selection
- Remove separate Reprint button and reprintCheck function
- Remove printed guard from PUT and DELETE endpoints
2026-03-12 21:12:08 -06:00
dependabot[bot] 0cd4ceb67e Bump multer from 1.4.5-lts.2 to 2.1.1
Bumps [multer](https://github.com/expressjs/multer) from 1.4.5-lts.2 to 2.1.1.
- [Release notes](https://github.com/expressjs/multer/releases)
- [Changelog](https://github.com/expressjs/multer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/expressjs/multer/compare/v1.4.5-lts.2...v2.1.1)

---
updated-dependencies:
- dependency-name: multer
  dependency-version: 2.1.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-13 00:01:46 +00:00
steve f827de9b1a Shift company name to logo x origin when no logo is present 2026-03-12 17:37:53 -06:00
steve 91958b5331 Remove MICR font diagnostic logging 2026-03-12 17:35:21 -06:00
steve 6b3c6be352 Track GnuMICR.ttf in git; remove font file gitignore rules
Font files were excluded by fonts/*.ttf and fonts/*.otf in .gitignore,
so GitHub Actions builds never had them and the container fell back to Courier.
Commit the TTF (more compatible with PDFKit than the PS-converted OTF).
2026-03-12 17:33:08 -06:00
steve e1a22bdd1c Add MICR font diagnostic logging to trace load failure 2026-03-12 16:29:44 -06:00
steve b3a1b3a40f cleared out some old info form readme.md that is no longer relevant to the project. 2026-03-12 16:25:51 -06:00
steve 27d580e967 Switch MICR font to TTF; fix multi-line rendering for bank info and address fields
- Use GnuMICR.ttf instead of .otf (OTF converted from PS Type 1 may not embed in PDFKit)
- Add renderLines() helper that splits on \n and places each line at explicit Y offset,
  so lineBreak:false is honored per-line and PDFKit cursor doesn't drift
- Include bank_name as first line of Bank Information field
2026-03-12 16:03:22 -06:00
steve c7ce87afd5 Fix MICR font path, date import, PDF button bug; clean up config; add TODO markers
- Hardcode GnuMICR.otf path in pdfService.js; remove MICR_FONT_PATH env var
- Fix normalizeDate to handle MM/DD/YY (2-digit year) and return null on no match
- Fix generatePdf button DOM bug: update span directly instead of overwriting textContent
- Remove .env.example and NTFY_URL from docker-compose (app has no required config)
- Remove redundant fonts volume mount from docker-compose (fonts bundled in image)
- Mark MVP TODO items complete; add // TODO comments in source for post-MVP features
- Update README: correct slot height, remove stale env var docs
2026-03-12 15:49:56 -06:00
steve f5b1292aff Fix check slot height to 3.5", remove separator lines between checks 2026-03-12 14:49:22 -06:00
steve 6d5ed93efd Switch to node:20-slim for reliable mdbtools support 2026-03-12 14:34:50 -06:00
steve 0bb3ceddf8 Update README to reflect current project state 2026-03-12 14:26:13 -06:00
steve 14e1e15226 Add first-run setup wizard with 3-step account configuration 2026-03-12 14:18:15 -06:00
steve 68300d0375 Add .mdb import button and modal with server-side migration runner 2026-03-12 14:15:13 -06:00
steve d7fde71e15 Add ntfy success/failure notifications to Docker build workflow 2026-03-12 13:56:40 -06:00
steve a52244af39 Fix missing latest tag causing Portainer deploy failure
docker-compose pulls :latest but the workflow only pushed branch-name
tags (e.g. :main) and SHA tags -- never :latest. Portainer stacks
reported "manifest unknown" when trying to pull the image.

Add type=raw,value=latest scoped to the default branch so pushes to
main now also tag the image as :latest.
2026-03-12 13:51:04 -06:00
steve dab0932c89 Pull image from Docker Hub instead of building locally 2026-03-12 13:22:19 -06:00
steve 179a8c23fe Add package-lock.json for reproducible Docker builds 2026-03-12 12:51:49 -06:00
steve e5e1569fa7 Fix IMAGE_NAME to use vars.DOCKERHUB_USERNAME 2026-03-12 12:44:06 -06:00
steve 20dd54682d Update Docker config: opt into Node.js 24 actions, rename to check-printing
- Add FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true to docker-build workflow
- Switch login step to vars.DOCKERHUB_USERNAME (non-secret)
- Rename docker-compose service, container, volume, and DB file to check-printing
2026-03-12 12:33:45 -06:00
steve 5522029c5e Correct docker-build variables 2026-03-12 12:05:23 -06:00
steve e252ddb952 Add full project structure: backend, frontend, Docker, and CI workflows
- Organize backend into src/ (routes/, services/, db/) per package.json entrypoint
- Add migrations/import-mdb.js for one-time .mdb → SQLite migration
- Add public/ frontend: check ledger table, slide-in new/edit panel, PDF generation
- Add docker/Dockerfile and docker-compose.yml for self-hosted deployment
- Add .github/workflows: Docker Hub build+push on main/tags, TODO→Issues scanner
- Add GnuMICR font files (GPL-2.0) for MICR E-13B line rendering
2026-03-12 10:29:36 -06:00
steve 9fcb31ba0d Initial commit 2026-03-12 09:34:50 -06:00