No description
  • HTML 56.1%
  • C# 34.9%
  • CSS 6%
  • JavaScript 3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-11 20:25:22 +02:00
src Restore antiforgery token on logout form (endpoint requires it) 2026-10-11 20:21:13 +02:00
.gitignore Ignore repo-root .env, document Forgejo push flow 2026-10-11 20:25:22 +02:00
BlazorERP.slnx Scaffold BlazorERP: Blazor Server (Individual auth, MudBlazor) + JWT API (EF Postgres) + Shared DTOs 2026-10-11 17:30:46 +02:00
README.md Ignore repo-root .env, document Forgejo push flow 2026-10-11 20:25:22 +02:00

BlazorERP — Construction ERP (starter)

Blazor Server web app + JWT-secured ASP.NET Core API + Postgres, on .NET 10. UI shell uses MudBlazor 9 (amber-on-slate construction theme).

BlazorERP.slnx
src/
  BlazorERP.Web/      STEP 1 — Blazor Web App (Server interactivity, SSR pages,
                      Individual cookie auth, MudBlazor shell)
  BlazorERP.Api/      STEP 2 — API (JWT bearer tokens, Projects CRUD, Scalar docs)
  BlazorERP.Shared/   Single Postgres database (Identity users + ERP entities),
                      EF migrations, DTOs and config shared by Web + API

One Postgres database, one user store: registering in the Web app creates the same Identity row the API logs in with, and vice versa.

Prerequisites

  • .NET 10 SDK (dotnet --version → 10.x)
  • Postgres 15+ (local install or Docker: docker run -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:16)

1. Configure the API database (.env)

Edit src/BlazorERP.Api/.env (git-ignored; see .env.example for the template):

POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=blazorerp
POSTGRES_USER=postgres
POSTGRES_PASSWORD=<your password>
JWT_KEY=<min 32 chars, e.g. output of: openssl rand -base64 48>

Create the database once: createdb -U postgres blazorerp (or it must exist — the API creates tables itself in Development via EnsureCreated).

Production: use dotnet ef migrations + Database.Migrate() instead of EnsureCreated. The Design package is already referenced in the API project.

2. Run the API (STEP 2)

dotnet run --project src/BlazorERP.Api

Try it:

curl -k -X POST https://localhost:7095/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@blazorerp.local","password":"Admin123!"}'
# → {"token":"...","expiresAtUtc":"...","email":"..."}

3. Run the Blazor app (STEP 1)

New terminal:

dotnet run --project src/BlazorERP.Web
  • App: https://localhost:7268
  • Register a local Individual account via Register (top-right, SQLite-backed cookie auth), or just browse the Dashboard.
  • Open Projects in the sidebar → sign in with the API seed login above → searchable/filterable/sortable MudDataGrid with full CRUD against Postgres.

The Web app calls the API at Api:BaseUrl (src/BlazorERP.Web/appsettings.json, default https://localhost:7095).

How auth fits together

  • One user store: BlazorERP.Shared holds the single AppDbContext (Identity + ERP entities) in Postgres. Both apps use it, with the same Identity schema version — one registration works everywhere.
  • One login page: /Account/Login (MudBlazor) signs the Identity cookie in and fetches the API JWT in the same step. The JWT travels inside the cookie as a claim, so every page (Projects, Dashboard) is API-authenticated with no second login. Cookie-only sign-in still works if the API is down.
  • Web (STEP 1): standard Blazor Individual auth — cookie sign-in, full Register/Login/Manage pages (MudBlazor shell). RequireConfirmedAccount is off for local dev. Login supports passwords, 2FA authenticator codes, lockout and "remember me".
  • API (STEP 2): same Identity users plus JWT bearer tokens: POST /api/auth/register|login → token → Authorization: Bearer header.
  • Themes: five MudBlazor themes (Amber, Ocean, Forest, Plum, Midnight dark mode) via one MudThemeProvider, selectable under Account → Manage → Appearance and saved per user in Postgres.

Database migrations

Schema is owned by BlazorERP.Shared (one migration set for both apps). Run from the repo root:

dotnet ef migrations add <Name> --project src/BlazorERP.Shared --startup-project src/BlazorERP.Api
dotnet ef database update --project src/BlazorERP.Shared --startup-project src/BlazorERP.Api

The API also applies pending migrations automatically at startup in Development, then seeds the admin login + sample projects. Connection details come from src/BlazorERP.Api/.env, which the Web app reads too.

Project map

Path Purpose
Path Purpose
--- ---
src/BlazorERP.Web/Components/Layout MudBlazor shell (MainLayout, NavMenu), amber/slate theme
src/BlazorERP.Web/Components/Pages Home (dashboard), Projects (grid + CRUD)
src/BlazorERP.Web/Services ErpApiClient, ApiAuthState, ThemeService, ErpThemes (5 themes), DemoData fallback
src/BlazorERP.Api/Controllers AuthController, ProjectsController ([Authorize])
src/BlazorERP.Api/Services JwtTokenService
src/BlazorERP.Shared/Data AppDbContext (single store), DbSeeder
src/BlazorERP.Shared/Migrations EF migrations (both apps)
src/BlazorERP.Shared/Identity ApplicationUser (one user type)
src/BlazorERP.Shared/Erp Project entity + ProjectDto contracts
src/BlazorERP.Shared/Config .env loader + Postgres connection builder

Suggested next modules

Employees, Equipment/Plant, Timesheets, Purchase orders, Subcontractors — each = entity in AppDbContext + DTOs in Shared + Controller + Blazor page with MudDataGrid.

Pushing to Forgejo

Remote origin points at Forgejo. Auth uses the FORGEJO_TOKEN environment variable (repo credential helper is already configured — the token value is never stored in git). Keep the token in the git-ignored repo-root .env, then push with:

$env:FORGEJO_TOKEN = (Select-String '^FORGEJO_TOKEN=(.*)$' .env).Matches[0].Groups[1].Value
git push
Remove-Item Env:FORGEJO_TOKEN