9.3 KiB
💰 Budgeteer
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
Run Locally
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
dotnet test
Run with Docker
# 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.dbinside the container. Always mount a volume to/app/dataso 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
Configuration
Default connection string in appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=budgetapp.db"
}
}
Override at runtime via environment variable:
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.
{
"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:
-
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
-
Configure Certificate Paths The application will look for certificate files in the following order:
- Environment variables:
PEM_CERT_FILEandPEM_KEY_FILE - Configuration values:
PemCertFileandPemKeyFileinappsettings.json - Default:
/app/data/certs/cert.pemand/app/data/certs/key.pem
You can set environment variables before running the app:
export PEM_CERT_FILE=/path/to/cert.pem export PEM_KEY_FILE=/path/to/key.pemOr add to your
appsettings.json:{ "PemCertFile": "/path/to/cert.pem", "PemKeyFile": "/path/to/key.pem" } - Environment variables:
-
Fallback Behavior If the certificate files are not found, the app will use the default development certificate.
-
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.