# 💰 Budgeteer [![Build and Test](https://github.com/edgeofsanity76/Budgeteer/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/edgeofsanity76/Budgeteer/actions/workflows/build-and-test.yml) [![Docker Publish](https://github.com/edgeofsanity76/Budgeteer/actions/workflows/docker-publish.yml/badge.svg)](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 ``` ## 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.