287 lines
10 KiB
Markdown
287 lines
10 KiB
Markdown
# 💰 Budgeteer
|
|
|
|
[](https://git.oneputleygreen.com/rossmill76/Budgeteer/actions?workflow=build-and-test.yml)
|
|
[](https://github.com/edgeofsanity76/Budgeteer/actions/workflows/docker-publish.yml)
|
|
|
|
A personal finance dashboard built with Blazor Server (.NET 10)
|
|
|
|
> **Status:** beta — see the in-app badge.
|
|
|
|
## Features
|
|
|
|
- **Multi-account support** — current accounts, savings, credit cards, each with its own currency and colour
|
|
- **Transaction tracking** — income & expenses with date, category, and account assignment
|
|
- **Recurring transactions** — daily, weekly, monthly, or yearly recurrence with automatic materialisation up to one year ahead
|
|
- **Dashboard** — balance trend chart (Chart.js), recent & upcoming transaction lists, account filter toggles, inline balance editing, and configurable time window
|
|
- **Reports** — doughnut charts for expenses and income by category, monthly income vs expenses bar chart, and a category summary table with filterable date range and account selection
|
|
- **Categories** — emoji-icon categories with sensible defaults and full CRUD
|
|
- **Transactions table** — sortable columns, search, pagination, per-account filtering, time-window presets, bulk selection & delete, and responsive column hiding on mobile
|
|
- **Authentication** — ASP.NET Identity with registration, login, password change, account deletion, and 5-attempt lockout
|
|
- **SSO providers** — optional Google, Microsoft, and GitHub sign-in (configured via `appsettings.json`)
|
|
- **Settings** — change password, reset all data (with confirmation), delete account
|
|
- **Toast notifications** — inline validation feedback via a toast system
|
|
- **Help & About pages** — in-app guides, tips, and project info
|
|
- **Responsive UI** — Bootstrap 5, mobile-friendly layout with collapsible sidebar, responsive table columns, and scoped CSS
|
|
- **CI/CD** — GitHub Actions for build/test and Docker Hub publishing
|
|
- **Docker** — multi-stage Dockerfile with volume-mounted SQLite, OCI image labels, and automatic security patching
|
|
|
|
## Tech Stack
|
|
|
|
| Layer | Technology |
|
|
|---|---|
|
|
| Framework | .NET 10 / Blazor Server (InteractiveServer) |
|
|
| Database | SQLite via EF Core |
|
|
| Auth | ASP.NET Core Identity + optional Google / Microsoft / GitHub SSO |
|
|
| Charts | Chart.js (CDN) with JS interop |
|
|
| CSS | Bootstrap 5, scoped CSS |
|
|
| Tests | xUnit |
|
|
| CI/CD | GitHub Actions |
|
|
| Container | Docker (multi-stage build) |
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0)
|
|
|
|
### Run Locally
|
|
|
|
```bash
|
|
cd BudgetApp
|
|
dotnet run
|
|
```
|
|
|
|
The app will be available at `https://localhost:5001` (or the port shown in the console). A `budgetapp.db` SQLite file is created automatically on first run.
|
|
|
|
### Run Tests
|
|
|
|
```bash
|
|
dotnet test
|
|
```
|
|
|
|
### Run with Docker
|
|
|
|
```bash
|
|
# Build
|
|
docker build -t budgeteer .
|
|
|
|
# Run (persists database in a named volume)
|
|
docker run -d -p 8080:8080 -v budgeteer-data:/app/data --name budgeteer budgeteer
|
|
```
|
|
|
|
> **Note:** The SQLite database is stored at `/app/data/budgetapp.db` inside the container.
|
|
> Always mount a volume to `/app/data` so your data survives image updates and container replacements.
|
|
|
|
The app will be available at `http://localhost:8080`.
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
BudgetApp/
|
|
├── Components/
|
|
│ ├── Layout/
|
|
│ │ ├── MainLayout.razor
|
|
│ │ ├── MainLayout.razor.css
|
|
│ │ ├── AuthLayout.razor
|
|
│ │ ├── AuthLayout.razor.css
|
|
│ │ ├── NavMenu.razor
|
|
│ │ ├── NavMenu.razor.cs
|
|
│ │ └── NavMenu.razor.css
|
|
│ ├── Pages/
|
|
│ │ ├── Home.razor
|
|
│ │ ├── Home.razor.cs
|
|
│ │ ├── Home.razor.css
|
|
│ │ ├── Accounts.razor
|
|
│ │ ├── Accounts.razor.cs
|
|
│ │ ├── Transactions.razor
|
|
│ │ ├── Transactions.razor.cs
|
|
│ │ ├── Transactions.razor.css
|
|
│ │ ├── Categories.razor
|
|
│ │ ├── Categories.razor.cs
|
|
│ │ ├── Salary.razor
|
|
│ │ ├── Salary.razor.cs
|
|
│ │ ├── Salary.razor.css
|
|
│ │ ├── Reports.razor
|
|
│ │ ├── Reports.razor.cs
|
|
│ │ ├── About.razor
|
|
│ │ ├── About.razor.cs
|
|
│ │ ├── Settings.razor
|
|
│ │ ├── Settings.razor.cs
|
|
│ │ ├── Login.razor
|
|
│ │ ├── Login.razor.cs
|
|
│ │ ├── Register.razor
|
|
│ │ ├── Register.razor.cs
|
|
│ │ ├── Logout.razor
|
|
│ │ ├── Logout.razor.cs
|
|
│ │ ├── Help.razor
|
|
│ │ ├── NotFound.razor
|
|
│ │ ├── Error.razor
|
|
│ ├── ToastContainer.razor
|
|
│ ├── ToastContainer.razor.cs
|
|
│ ├── ToastContainer.razor.css
|
|
│ ├── ToastValidator.razor
|
|
│ ├── ToastValidator.razor.cs
|
|
│ └── _Imports.razor
|
|
├── Services/
|
|
│ ├── DashboardService.cs
|
|
│ ├── ToastService.cs
|
|
│ ├── AppState.cs
|
|
│ ├── Models/
|
|
│ │ ├── ChartData.cs
|
|
│ │ ├── ChartDataset.cs
|
|
│ │ └── DashboardData.cs
|
|
├── Program.cs
|
|
├── wwwroot/
|
|
│ ├── app.css
|
|
│ └── js/
|
|
│ └── chart-interop.js
|
|
├── appsettings.json
|
|
├── appsettings.Development.json
|
|
├── Dockerfile
|
|
├── .dockerignore
|
|
├── README.md
|
|
└── certs/
|
|
└── .gitkeep
|
|
|
|
BudgetApp.Data/
|
|
├── Entities/
|
|
│ ├── Account.cs
|
|
│ ├── ApplicationUser.cs
|
|
│ ├── Category.cs
|
|
│ ├── RecurrenceType.cs
|
|
│ ├── Salary.cs
|
|
│ ├── Transaction.cs
|
|
│ ├── TransactionType.cs
|
|
├── Interfaces/
|
|
│ ├── IAccountRepository.cs
|
|
│ ├── ICategoryRepository.cs
|
|
│ ├── ISalaryRepository.cs
|
|
│ ├── ITransactionRepository.cs
|
|
├── Repositories/
|
|
│ ├── AccountRepository.cs
|
|
│ ├── CategoryRepository.cs
|
|
│ ├── SalaryRepository.cs
|
|
│ └── TransactionRepository.cs
|
|
├── ApplicationDbContext.cs
|
|
├── Migrations/
|
|
│ ├── AddCurrencyCodeAndCategoryIcon.cs
|
|
│ ├── AddCurrencyCodeAndCategoryIcon.Designer.cs
|
|
│ ├── AddIsDefaultToCategory.cs
|
|
│ ├── AddIsDefaultToCategory.Designer.cs
|
|
│ ├── AddIsPaidToTransaction.cs
|
|
│ ├── AddIsPaidToTransaction.Designer.cs
|
|
│ ├── AddRecurringTransactions.cs
|
|
│ ├── AddRecurringTransactions.Designer.cs
|
|
│ ├── AddSalary.cs
|
|
│ ├── AddSalary.Designer.cs
|
|
│ ├── InitialCreate.cs
|
|
│ ├── InitialCreate.Designer.cs
|
|
│ └── ApplicationDbContextModelSnapshot.cs
|
|
|
|
BudgetApp.Tests/
|
|
├── Helpers/
|
|
│ └── TestDbContextFactory.cs
|
|
├── Models/
|
|
│ └── AccountModelTests.cs
|
|
├── Services/
|
|
│ ├── AccountServiceTests.cs
|
|
│ ├── CategoryServiceTests.cs
|
|
│ ├── DashboardServiceTests.cs
|
|
│ └── TransactionServiceTests.cs
|
|
```
|
|
|
|
### Project Roles
|
|
|
|
- **BudgetApp**: Main Blazor Server application. Contains the UI, interactive pages, layouts, authentication logic, and application services. This is the entry point for running the app and includes configuration, dependency injection, and SSL setup.
|
|
|
|
- **BudgetApp.Data**: Data access layer. Provides EF Core entities, the ApplicationDbContext, repository interfaces and implementations, and database migrations. Handles all database operations and schema management.
|
|
|
|
- **BudgetApp.Tests**: Unit tests for services, models, and data access. Ensures correctness of business logic and data access. Uses xUnit for test execution.
|
|
|
|
## Configuration
|
|
|
|
Default connection string in `appsettings.json`:
|
|
|
|
```json
|
|
{
|
|
"ConnectionStrings": {
|
|
"DefaultConnection": "Data Source=budgetapp.db"
|
|
}
|
|
}
|
|
```
|
|
|
|
Override at runtime via environment variable:
|
|
|
|
```bash
|
|
export ConnectionStrings__DefaultConnection="Data Source=/path/to/budgeteer.db"
|
|
```
|
|
|
|
### SSO Providers (optional)
|
|
|
|
Add client credentials to `appsettings.json` (or via environment / user-secrets). Each provider is only registered when both `ClientId` and `ClientSecret` are non-empty.
|
|
|
|
```json
|
|
{
|
|
"Authentication": {
|
|
"Google": {
|
|
"ClientId": "",
|
|
"ClientSecret": ""
|
|
},
|
|
"Microsoft": {
|
|
"ClientId": "",
|
|
"ClientSecret": ""
|
|
},
|
|
"GitHub": {
|
|
"ClientId": "",
|
|
"ClientSecret": ""
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Licence
|
|
|
|
This project is provided as-is for personal use.
|
|
## Using Custom SSL Certificates
|
|
|
|
To enable HTTPS with custom SSL certificates in your Blazor application, follow these steps:
|
|
|
|
1. **Prepare PEM Files**
|
|
Place your certificate and private key files in PEM format in a directory accessible by your app.
|
|
Example paths:
|
|
- `/app/data/certs/cert.pem`
|
|
- `/app/data/certs/key.pem`
|
|
|
|
2. **Configure Certificate Paths**
|
|
The application will look for certificate files in the following order:
|
|
- Environment variables: `PEM_CERT_FILE` and `PEM_KEY_FILE`
|
|
- Configuration values: `PemCertFile` and `PemKeyFile` in `appsettings.json`
|
|
- Default: `/app/data/certs/cert.pem` and `/app/data/certs/key.pem`
|
|
|
|
You can set environment variables before running the app:
|
|
```bash
|
|
export PEM_CERT_FILE=/path/to/cert.pem
|
|
export PEM_KEY_FILE=/path/to/key.pem
|
|
```
|
|
|
|
Or add to your `appsettings.json`:
|
|
```json
|
|
{
|
|
"PemCertFile": "/path/to/cert.pem",
|
|
"PemKeyFile": "/path/to/key.pem"
|
|
}
|
|
```
|
|
|
|
3. **Fallback Behavior**
|
|
If the certificate files are not found, the app will use the default development certificate.
|
|
|
|
4. **No Code Changes Needed**
|
|
The certificate loading logic is already implemented in `Program.cs`. Simply provide the files and configuration as described above.
|
|
|
|
---
|
|
|
|
**Note:**
|
|
- Certificates must be in PEM format (`.pem`).
|
|
- Ensure file permissions allow the app to read the certificate and key files.
|
|
- For production, use certificates issued by a trusted Certificate Authority.
|