- HTML 56.1%
- C# 34.9%
- CSS 6%
- JavaScript 3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| src | ||
| .gitignore | ||
| BlazorERP.slnx | ||
| README.md | ||
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 ofEnsureCreated. TheDesignpackage is already referenced in the API project.
2. Run the API (STEP 2)
dotnet run --project src/BlazorERP.Api
- API docs (Scalar): https://localhost:7095/scalar/v1
- OpenAPI JSON: https://localhost:7095/openapi/v1.json
- First run seeds: admin login
admin@blazorerp.local/Admin123!+ 6 sample projects.
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
MudDataGridwith 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.Sharedholds the singleAppDbContext(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).
RequireConfirmedAccountis 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: Bearerheader. - 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