- Add PrintJob types (PrintJob, PrintJobFilter) with status union type - Add printJobService.ts using axios + TanStack Query - Build PrintJobsPage.tsx with: - Desktop table: job name, printer, color-coded status badge, started time, duration, filament used, cost - Mobile card layout with grid-based info display - Filter by status (pending/printing/completed/failed) - Filter by printer name - Sort by any column (name, started_at, duration, filament, cost) - Pagination with 20 per page - Loading spinner, error with retry, empty state - Client-side text search by job/printer name - Duration formatting (h/m/s) - Add navigation bar in App.tsx with Inventory + Print Jobs tabs using NavLink
Extrudex
Filament inventory and print tracking system for CubeCraft Creations.
Extrudex replaces Spoolman with a fully custom solution built for Joshua's 7-printer fleet. It tracks spool stock, per-print material consumption, and cost-of-goods — with a touch-optimized kiosk interface on a Raspberry Pi 5.
Tech Stack
| Layer | Technology |
|---|---|
| Backend | ASP.NET Core Web API (.NET 8) |
| Database | PostgreSQL (snake_case via EF Core) |
| ORM | Entity Framework Core |
| Real-time | SignalR (PrinterHub) |
| Printer integration | Moonraker REST/WebSocket (Elegoo) · MQTTnet + TLS (Bambu Lab) |
| Frontend | Angular 17+, Angular Material |
| Deployment | Docker · Docker Compose |
Project Structure
Extrudex/
├── backend/
│ ├── Domain/
│ │ ├── Base/ # BaseEntity, AuditableEntity
│ │ ├── Entities/ # Spool, Printer, PrintJob, FilamentUsage,
│ │ │ # AmsUnit, AmsSlot, MaterialBase,
│ │ │ # MaterialFinish, MaterialModifier
│ │ ├── Enums/ # ConnectionType, DataSource, JobStatus,
│ │ │ # PrinterStatus, PrinterType, QrResourceType
│ │ └── Interfaces/ # ICostPerPrintService, IFilamentUsageSyncService,
│ │ # IMoonrakerClient, IQrCodeService
│ ├── Infrastructure/
│ │ ├── Configuration/ # FilamentUsageSyncOptions
│ │ ├── Data/
│ │ │ ├── Configurations/ # EF Core fluent configs (snake_case)
│ │ │ ├── Migrations/ # EF migrations
│ │ │ ├── Seed/ # SeedData.cs
│ │ │ └── ExtrudexDbContext.cs
│ │ └── Services/ # CostPerPrintService, FilamentUsageSyncService,
│ │ # MoonrakerClient, QrCodeService
│ └── API/
│ ├── Controllers/ # Filaments, Spools, Printers, PrintJobs,
│ │ # MaterialBases, MaterialFinishes,
│ │ # MaterialModifiers, MaterialLookups,
│ │ # CostAnalysis, QR
│ ├── DTOs/ # Request/response shapes per domain
│ ├── Filters/ # FluentValidationFilter
│ ├── Hubs/ # PrinterHub, IPrinterClient
│ ├── Jobs/ # FilamentUsageSyncJob (background)
│ ├── Validators/ # FluentValidation validators
│ ├── Program.cs
│ └── appsettings.json
├── frontend/
│ └── src/app/
│ ├── components/ # DashboardSummary, FilamentFilter, FilamentTable
│ ├── models/ # Filament, Agent model types
│ └── app.routes.ts
├── design/ # UX specs and mockups (kiosk + mobile)
├── docker-compose.dev.yml
├── deploy.sh
└── README.md
Domain Model
Materials (normalized taxonomy)
| Entity | Description |
|---|---|
MaterialBase |
The base material type — PLA, PETG, ABS, ASA, TPU, etc. |
MaterialFinish |
Required. Surface finish — Basic (default), Matte, Silk, Sparkle, etc. |
MaterialModifier |
Optional. Composite fill — Carbon Fiber, Glass Fiber, Wood, etc. |
Rules:
MaterialFinishis required — every spool must have one. Default is"Basic".MaterialModifieris optional — plain PLA has no modifier.
Consumption calculation
grams_used = mm_extruded × filament_cross_section_area × material_density
Grams are always derived, never assumed from printer telemetry directly.
Printers
| Type | Integration |
|---|---|
| Bambu Lab (×5) | MQTTnet with TLS |
| Elegoo Centauri Carbon | Moonraker REST + WebSocket |
| Elegoo Saturn (resin ×2) | Manual / future |
AMS units and slots are modelled as AmsUnit → AmsSlot[] → Spool.
Key Design Decisions
- Spoolman rejected — Full custom system for data model control and workflow flexibility.
"Basic"not"Standard"— DefaultMaterialFinishvalue isBasic.MaterialFinishis required — No null/optional finish state allowed.MaterialModifieris optional — Not every spool has a modifier.- Derived consumption — Grams calculated from mm × density, never assumed.
- Push over poll — SignalR and MQTT preferred over periodic polling.
- Snake_case PostgreSQL — All database identifiers follow this convention via EF Core.
Getting Started
Prerequisites
- .NET 8 SDK
- Node.js 20+
- Docker + Docker Compose
- PostgreSQL (or use the dev compose stack)
Backend
cd backend
# Restore and build
dotnet restore
dotnet build
# Apply migrations
dotnet ef database update
# Run API (dev)
dotnet run --project API
API runs at http://localhost:5000 · Swagger at http://localhost:5000/swagger
Frontend
cd frontend
npm install
ng serve
Frontend runs at http://localhost:4200
Docker (dev stack)
docker-compose -f docker-compose.dev.yml up
Configuration
backend/appsettings.json — override in appsettings.Development.json or environment variables:
| Key | Default | Description |
|---|---|---|
ConnectionStrings:ExtrudexDb |
Host=localhost;... |
PostgreSQL connection string |
FilamentUsageSync:PollingInterval |
00:05:00 |
Sync job interval |
FilamentUsageSync:RequestTimeout |
00:00:30 |
Moonraker request timeout |
FilamentUsageSync:Enabled |
true |
Enable/disable background sync |
Real-Time Events
SignalR hub endpoint: /hubs/printer
Clients receive PrinterHub events for live printer status, job progress, and spool consumption updates.
API Overview
| Route prefix | Resource |
|---|---|
/api/filaments |
Filament catalog |
/api/spools |
Spool inventory |
/api/printers |
Printer registry |
/api/print-jobs |
Print job tracking |
/api/material-bases |
Material base types |
/api/material-finishes |
Material finishes |
/api/material-modifiers |
Material modifiers |
/api/material-lookups |
Combined material lookup |
/api/cost-analysis |
Cost-per-print and COGS |
/api/qr |
QR code generation |
Full schema available at /swagger when running in dev.
CI
Gitea Actions pipeline (.gitea/workflows/dev.yml) runs on every push to dev:
dotnet build- Frontend
ng build
Branch & PR Rules
- All feature branches target
dev— nevermain - Branch naming:
agent/<agent>/CUB-N-short-description - PR titles:
CUB-N: short description - PRs require Otto review before Joshua merges
Built by CubeCraft Creations · Orchestrated by Otto