init
This commit is contained in:
parent
7cbdefbcfe
commit
678ff5eb19
624
README.md
624
README.md
|
|
@ -1 +1,623 @@
|
||||||
first.
|
# Bill Tracker
|
||||||
|
|
||||||
|
Bill Tracker is a self-hosted month-to-month bill tracking app for managing recurring bills, payments, monthly bill history, user-owned financial data, imports/exports, backups, and admin operations.
|
||||||
|
|
||||||
|
The app is built as a full-stack JavaScript project with a React/Vite frontend, Node/Express backend, and SQLite database.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Monthly Bill Tracking
|
||||||
|
|
||||||
|
- Track bills by selected month and year
|
||||||
|
- View active bills for the current month
|
||||||
|
- Record payments
|
||||||
|
- Track actual monthly bill amounts
|
||||||
|
- Add month-specific notes
|
||||||
|
- Mark bills as skipped for a specific month
|
||||||
|
- Separate global bill templates from monthly bill state
|
||||||
|
|
||||||
|
### Bills
|
||||||
|
|
||||||
|
- Create and edit recurring bill templates
|
||||||
|
- Bill due day is stored as a day of the month, such as `1`, `15`, or `31`
|
||||||
|
- Supports month-end clamping for due days like `31`
|
||||||
|
- Optional credit card interest/APR field
|
||||||
|
- Active/inactive bill support
|
||||||
|
- Safe deactivate/reactivate flow
|
||||||
|
- Destructive delete flow with confirmation
|
||||||
|
- Historical visibility metadata and range support for inactive bills
|
||||||
|
|
||||||
|
### Payments
|
||||||
|
|
||||||
|
- Add, update, delete, and restore payments
|
||||||
|
- Payments are scoped to the signed-in user
|
||||||
|
- Payment data is included in tracker totals and exports
|
||||||
|
- Payment history remains separate from global bill templates
|
||||||
|
|
||||||
|
### Categories
|
||||||
|
|
||||||
|
- User-owned categories
|
||||||
|
- Categories can be associated with bills
|
||||||
|
- Category bill counts are supported
|
||||||
|
- Categories are scoped to the signed-in user
|
||||||
|
|
||||||
|
### Monthly Bill State
|
||||||
|
|
||||||
|
Each bill can have month-specific state:
|
||||||
|
|
||||||
|
- `actual_amount`
|
||||||
|
- `notes`
|
||||||
|
- `is_skipped`
|
||||||
|
- year/month association
|
||||||
|
|
||||||
|
This allows one bill template to have separate history for every month.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Accounts and Profile
|
||||||
|
|
||||||
|
Bill Tracker supports local username/password accounts and optional authentik/OIDC login.
|
||||||
|
|
||||||
|
Profile features include:
|
||||||
|
|
||||||
|
- Display name
|
||||||
|
- Notification preferences
|
||||||
|
- Password change
|
||||||
|
- User data exports
|
||||||
|
- User SQLite import
|
||||||
|
- Spreadsheet import history
|
||||||
|
|
||||||
|
The display name is shown in the top navigation where available. If no display name is set, the app falls back to the username.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
### Local Login
|
||||||
|
|
||||||
|
Local username/password login is supported and remains available by default.
|
||||||
|
|
||||||
|
Local users can:
|
||||||
|
|
||||||
|
- log in with username/password
|
||||||
|
- change their password
|
||||||
|
- complete first-login password reset flows
|
||||||
|
- use all normal user-owned bill tracking features
|
||||||
|
|
||||||
|
### authentik / OIDC Login
|
||||||
|
|
||||||
|
The backend is prepared for authentik using OpenID Connect.
|
||||||
|
|
||||||
|
Supported OIDC behavior:
|
||||||
|
|
||||||
|
- Authorization Code flow
|
||||||
|
- PKCE
|
||||||
|
- state validation
|
||||||
|
- nonce validation
|
||||||
|
- JWKS-backed ID token signature verification via `openid-client`
|
||||||
|
- issuer validation
|
||||||
|
- audience validation
|
||||||
|
- expiration validation
|
||||||
|
- authentik group-to-role mapping
|
||||||
|
- auto-provisioning of valid authentik users
|
||||||
|
- local session creation after successful OIDC login
|
||||||
|
|
||||||
|
Admin role is never granted by default. A user becomes admin through the configured authentik admin group only.
|
||||||
|
|
||||||
|
### Admin-Controlled Login Methods
|
||||||
|
|
||||||
|
Admins can configure login methods from the Admin panel:
|
||||||
|
|
||||||
|
- Enable/disable local username/password login
|
||||||
|
- Enable/disable authentik/OIDC login
|
||||||
|
- Configure authentik issuer URL
|
||||||
|
- Configure client ID
|
||||||
|
- Configure client secret
|
||||||
|
- Configure redirect URI
|
||||||
|
- Configure scopes
|
||||||
|
- Configure admin group
|
||||||
|
- Configure auto-provisioning
|
||||||
|
|
||||||
|
Lockout protection prevents disabling local login unless authentik is configured, enabled, and has an admin group mapping.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Ownership
|
||||||
|
|
||||||
|
Bill Tracker uses user-owned data separation.
|
||||||
|
|
||||||
|
The following data is scoped to the signed-in user:
|
||||||
|
|
||||||
|
- Bills
|
||||||
|
- Categories
|
||||||
|
- Payments
|
||||||
|
- Monthly bill state
|
||||||
|
- Imports
|
||||||
|
- Exports
|
||||||
|
- Tracker views
|
||||||
|
|
||||||
|
Users cannot select another `user_id` from the client. Backend routes derive ownership from the authenticated session.
|
||||||
|
|
||||||
|
Admin full database backup/restore is separate from user-owned import/export.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Import and Export
|
||||||
|
|
||||||
|
### XLSX Spreadsheet Import
|
||||||
|
|
||||||
|
Bill Tracker supports importing historical bill data from Google Sheets `.xlsx` exports.
|
||||||
|
|
||||||
|
The XLSX import flow includes:
|
||||||
|
|
||||||
|
- Upload spreadsheet
|
||||||
|
- Preview before applying
|
||||||
|
- Multi-sheet parsing
|
||||||
|
- Month/year detection from sheet names
|
||||||
|
- Bill match recommendations
|
||||||
|
- Match existing bill
|
||||||
|
- Create new bill
|
||||||
|
- Skip row
|
||||||
|
- Bulk skip
|
||||||
|
- Bulk create new bills
|
||||||
|
- Ambiguous row blocking
|
||||||
|
- Import history
|
||||||
|
|
||||||
|
Supported sheet/tab patterns include examples like:
|
||||||
|
|
||||||
|
- `Jan 2026`
|
||||||
|
- `January 2026`
|
||||||
|
- `2026-01`
|
||||||
|
- `01-2026`
|
||||||
|
- `2026 May`
|
||||||
|
- `Bills May 2026`
|
||||||
|
- `May` with default year
|
||||||
|
|
||||||
|
Non-data sheets like `Summary`, `Totals`, `Dashboard`, `Notes`, `Categories`, `Settings`, `Overview`, and `Template` are skipped or treated as non-month sheets.
|
||||||
|
|
||||||
|
### User SQLite Export
|
||||||
|
|
||||||
|
Users can export their own data as a SQLite database.
|
||||||
|
|
||||||
|
The user export includes only that user’s safe bill-tracker data, such as:
|
||||||
|
|
||||||
|
- Bills
|
||||||
|
- Categories
|
||||||
|
- Payments
|
||||||
|
- Monthly bill state
|
||||||
|
- Notes
|
||||||
|
- Export metadata
|
||||||
|
|
||||||
|
It does not include:
|
||||||
|
|
||||||
|
- Password hashes
|
||||||
|
- Sessions
|
||||||
|
- Cookies
|
||||||
|
- Admin settings
|
||||||
|
- SMTP credentials
|
||||||
|
- Backup files
|
||||||
|
- Server paths
|
||||||
|
- Other users’ data
|
||||||
|
|
||||||
|
### User SQLite Import
|
||||||
|
|
||||||
|
Users can import a SQLite export created by this app.
|
||||||
|
|
||||||
|
The user SQLite import flow includes:
|
||||||
|
|
||||||
|
- Upload user SQLite export
|
||||||
|
- Validate SQLite file
|
||||||
|
- Confirm export format is `user_data`
|
||||||
|
- Preview before apply
|
||||||
|
- Apply only after confirmation
|
||||||
|
- Create missing records
|
||||||
|
- Skip conflicts by default
|
||||||
|
- No overwrite by default
|
||||||
|
- Import history recording
|
||||||
|
|
||||||
|
This is not a full-system restore and does not use the admin backup import path.
|
||||||
|
|
||||||
|
### Excel Databook Export
|
||||||
|
|
||||||
|
Users can export an Excel workbook containing their own bill-tracker data for records and review.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Admin Tools
|
||||||
|
|
||||||
|
Admin users have access to an Admin area while still keeping access to the normal user app.
|
||||||
|
|
||||||
|
Admin tools include:
|
||||||
|
|
||||||
|
### User/Admin Management
|
||||||
|
|
||||||
|
- Admin-only routes
|
||||||
|
- Admin-only navigation
|
||||||
|
- Normal app navigation remains available for admins
|
||||||
|
|
||||||
|
### Authentication Methods
|
||||||
|
|
||||||
|
Admins can configure:
|
||||||
|
|
||||||
|
- Local login enabled/disabled
|
||||||
|
- authentik/OIDC login enabled/disabled
|
||||||
|
- authentik provider settings
|
||||||
|
- auto-provisioning
|
||||||
|
- admin group mapping
|
||||||
|
|
||||||
|
### Database Backups
|
||||||
|
|
||||||
|
Admin full database tools are separate from user data tools.
|
||||||
|
|
||||||
|
Admin backup features include:
|
||||||
|
|
||||||
|
- Manual database backup
|
||||||
|
- Backup listing
|
||||||
|
- Backup download
|
||||||
|
- Backup restore
|
||||||
|
- Pre-restore backup creation
|
||||||
|
- SQLite integrity checks
|
||||||
|
- Path traversal protection
|
||||||
|
- Managed backup directory
|
||||||
|
|
||||||
|
Full database backups are admin-only.
|
||||||
|
|
||||||
|
### Cleanup and Maintenance
|
||||||
|
|
||||||
|
Admin cleanup tools include:
|
||||||
|
|
||||||
|
- Expired import session cleanup
|
||||||
|
- Temporary export file cleanup
|
||||||
|
- Backup partial cleanup
|
||||||
|
- Optional import history trimming
|
||||||
|
- Manual cleanup run
|
||||||
|
- Cleanup status and last-result display
|
||||||
|
|
||||||
|
Cleanup tasks run through the daily worker and are non-fatal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status Page
|
||||||
|
|
||||||
|
The Status page provides safe read-only operational information.
|
||||||
|
|
||||||
|
It may show:
|
||||||
|
|
||||||
|
- App status
|
||||||
|
- Runtime status
|
||||||
|
- Database status
|
||||||
|
- Daily worker status
|
||||||
|
- Maintenance cleanup status
|
||||||
|
- Notification status
|
||||||
|
- Backup status
|
||||||
|
- Server clock
|
||||||
|
- Tracker health
|
||||||
|
- Recent errors
|
||||||
|
|
||||||
|
The Status page does not expose:
|
||||||
|
|
||||||
|
- Admin controls
|
||||||
|
- Cleanup settings controls
|
||||||
|
- Secrets
|
||||||
|
- Stack traces
|
||||||
|
- Absolute filesystem paths
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Bill Tracker includes several security hardening measures:
|
||||||
|
|
||||||
|
- Cookie-based authentication
|
||||||
|
- Production cookie secure flag support
|
||||||
|
- `sameSite: strict`
|
||||||
|
- Route-level authentication middleware
|
||||||
|
- Admin-only middleware
|
||||||
|
- User ownership enforcement
|
||||||
|
- Rate limiting on sensitive endpoints
|
||||||
|
- CORS allowlist support through configuration
|
||||||
|
- Global security headers
|
||||||
|
- Settings filtering to avoid leaking secrets
|
||||||
|
- Backup path traversal protection
|
||||||
|
- SQLite integrity check before restore
|
||||||
|
- User imports scoped to signed-in user
|
||||||
|
- User exports exclude auth/admin/system data
|
||||||
|
- OIDC token verification through `openid-client`
|
||||||
|
- authentik admin role mapping is explicit and not granted by default
|
||||||
|
- Error responses avoid leaking stack traces and internal paths
|
||||||
|
|
||||||
|
### Known Security Notes
|
||||||
|
|
||||||
|
- Downloaded exports and backups contain sensitive financial data and should be protected.
|
||||||
|
- Admin full database backups are not encrypted by default.
|
||||||
|
- CSP is deferred because it requires auditing Vite, Tailwind, Radix, and shadcn output.
|
||||||
|
- authentik live login must be tested against a real authentik instance in the target deployment.
|
||||||
|
- OIDC single logout is not currently implemented.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
- React
|
||||||
|
- Vite
|
||||||
|
- Tailwind CSS
|
||||||
|
- shadcn/Radix-style components
|
||||||
|
- Material Design shadcn theme
|
||||||
|
- OKLCH theme tokens
|
||||||
|
- lucide-react icons
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
- Node.js
|
||||||
|
- Express
|
||||||
|
- CommonJS modules
|
||||||
|
- better-sqlite3
|
||||||
|
- bcryptjs
|
||||||
|
- cookie-parser
|
||||||
|
- express-rate-limit
|
||||||
|
- openid-client
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
- SQLite
|
||||||
|
- App-managed schema/migrations on startup
|
||||||
|
- User-owned relational data
|
||||||
|
- Admin backup support
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
Typical project structure:
|
||||||
|
|
||||||
|
```text
|
||||||
|
client/
|
||||||
|
components/
|
||||||
|
contexts/
|
||||||
|
lib/
|
||||||
|
pages/
|
||||||
|
api.js
|
||||||
|
|
||||||
|
db/
|
||||||
|
database.js
|
||||||
|
schema.sql
|
||||||
|
|
||||||
|
middleware/
|
||||||
|
requireAuth.js
|
||||||
|
rateLimiter.js
|
||||||
|
securityHeaders.js
|
||||||
|
|
||||||
|
routes/
|
||||||
|
auth.js
|
||||||
|
authOidc.js
|
||||||
|
admin.js
|
||||||
|
bills.js
|
||||||
|
categories.js
|
||||||
|
payments.js
|
||||||
|
tracker.js
|
||||||
|
profile.js
|
||||||
|
import.js
|
||||||
|
export.js
|
||||||
|
settings.js
|
||||||
|
status.js
|
||||||
|
version.js
|
||||||
|
|
||||||
|
services/
|
||||||
|
authService.js
|
||||||
|
backupService.js
|
||||||
|
cleanupService.js
|
||||||
|
notificationService.js
|
||||||
|
oidcService.js
|
||||||
|
statusService.js
|
||||||
|
userDbImportService.js
|
||||||
|
|
||||||
|
workers/
|
||||||
|
dailyWorker.js
|
||||||
|
|
||||||
|
scripts/
|
||||||
|
test-import.js
|
||||||
|
test-oidc-smoke.js
|
||||||
|
|
||||||
|
setup/
|
||||||
|
firstRun.js
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the app in development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Run only the API in development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev:api
|
||||||
|
```
|
||||||
|
|
||||||
|
Run only the UI in development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev:ui
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the frontend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the production server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
npm run dev:api
|
||||||
|
npm run dev:ui
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful test/smoke scripts if present:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node scripts/test-import.js
|
||||||
|
node scripts/test-oidc-smoke.js
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Common environment variables may include:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PORT=3000
|
||||||
|
NODE_ENV=production
|
||||||
|
HTTPS=true
|
||||||
|
CORS_ORIGIN=https://bills.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### authentik / OIDC
|
||||||
|
|
||||||
|
authentik can now be configured from the Admin panel. Environment variables may still be used as fallback/bootstrap values when database settings are blank.
|
||||||
|
|
||||||
|
Possible OIDC-related variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OIDC_ENABLED=true
|
||||||
|
OIDC_ISSUER_URL=https://auth.example.com/application/o/bill-tracker/
|
||||||
|
OIDC_CLIENT_ID=your-client-id
|
||||||
|
OIDC_CLIENT_SECRET=your-client-secret
|
||||||
|
OIDC_REDIRECT_URI=https://bills.example.com/api/auth/oidc/callback
|
||||||
|
OIDC_SCOPES="openid email profile groups"
|
||||||
|
OIDC_ADMIN_GROUP=bill-tracker-admins
|
||||||
|
OIDC_DEFAULT_ROLE=user
|
||||||
|
OIDC_AUTO_PROVISION=true
|
||||||
|
OIDC_PROVIDER_NAME=authentik
|
||||||
|
```
|
||||||
|
|
||||||
|
Database-backed Admin settings take precedence over environment fallback values once configured.
|
||||||
|
|
||||||
|
Never expose `OIDC_CLIENT_SECRET` to the frontend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## authentik Setup
|
||||||
|
|
||||||
|
In authentik:
|
||||||
|
|
||||||
|
1. Create an OAuth2/OpenID Provider.
|
||||||
|
2. Use a confidential client.
|
||||||
|
3. Use Authorization Code flow.
|
||||||
|
4. Set redirect URI to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://your-domain.example/api/auth/oidc/callback
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Include scopes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
openid email profile groups
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Ensure authentik sends a `groups` claim.
|
||||||
|
7. Create or choose an authentik group for Bill Tracker admins.
|
||||||
|
8. Configure the same admin group in Bill Tracker Admin settings.
|
||||||
|
9. Keep local login enabled until authentik login is tested.
|
||||||
|
10. Only disable local login after confirming an authentik admin can access the Admin panel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Model Highlights
|
||||||
|
|
||||||
|
Core data areas include:
|
||||||
|
|
||||||
|
- `users`
|
||||||
|
- `bills`
|
||||||
|
- `categories`
|
||||||
|
- `payments`
|
||||||
|
- `monthly_bill_state`
|
||||||
|
- `bill_history_ranges`
|
||||||
|
- `import_sessions`
|
||||||
|
- `import_history`
|
||||||
|
- `settings`
|
||||||
|
- `oidc_states`
|
||||||
|
|
||||||
|
Bills are recurring templates. Month-specific values belong in monthly bill state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup vs Export
|
||||||
|
|
||||||
|
Bill Tracker intentionally separates full-system backups from user exports.
|
||||||
|
|
||||||
|
### Admin Backup
|
||||||
|
|
||||||
|
- Full SQLite database
|
||||||
|
- Admin-only
|
||||||
|
- Can be restored
|
||||||
|
- Includes system data
|
||||||
|
- Used for disaster recovery
|
||||||
|
|
||||||
|
### User Export
|
||||||
|
|
||||||
|
- Signed-in user’s own data only
|
||||||
|
- Can be downloaded by the user
|
||||||
|
- Can be imported back through user SQLite import
|
||||||
|
- Does not include auth/admin/system data
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development Notes
|
||||||
|
|
||||||
|
When changing the app:
|
||||||
|
|
||||||
|
- Keep Profile as the user/account/data hub.
|
||||||
|
- Keep Settings focused on app-level preferences.
|
||||||
|
- Keep Admin tools admin-only.
|
||||||
|
- Keep user import/export separate from admin backup/restore.
|
||||||
|
- Do not expose admin backup tools to regular users.
|
||||||
|
- Keep user ownership checks on all object routes.
|
||||||
|
- Use existing Tailwind/shadcn/Radix patterns.
|
||||||
|
- Update `HISTORY.md` when behavior changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
Known limitations:
|
||||||
|
|
||||||
|
- Admin backup encryption is not implemented.
|
||||||
|
- CSP is not fully implemented.
|
||||||
|
- authentik live flow requires testing against a real authentik instance.
|
||||||
|
- OIDC single logout is not implemented.
|
||||||
|
- Rate limiting uses in-memory storage, suitable for single-instance deployments.
|
||||||
|
- XLSX parsing remains a sensitive area and is mitigated through authentication, file limits, validation, and preview/apply flow.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
License: Not specified.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue