A modern, open-source Learning Management System (LMS) built specifically for the .NET ecosystem. TrainingApp enables organizations to deliver corporate training, track employee progress, and issue digitally-signed certificates - all while maintaining full control over their data through self-hosted deployment.
- 📖 Overview
- ✨ Key Features
- 📸 Screenshots
- 🛠️ Technology Stack
- 🚀 Getting Started
- 🏗️ Architecture
- ⚙️ Configuration
- 🚢 Deployment
- 📚 API Documentation
- 🧪 Testing
- 🤝 Contributing
- 📄 License
- 🏷️ Version
- 🙏 Acknowledgments
TrainingApp addresses the gap in open-source Learning Management Systems for organizations standardized on Microsoft technologies. Unlike popular LMS platforms built on PHP, Python, or Ruby, TrainingApp is native to the .NET ecosystem, reducing operational overhead and integration complexity for .NET-focused organizations.
The platform was developed as part of a diploma thesis analyzing corporate training requirements, digital signature integration, and data sovereignty concerns. It provides a complete solution for:
- Corporate Training Management - Create courses, assign users, track completion
- Knowledge Assessment - Build tests with multiple question types and automated grading
- Compliance Certification - Generate PDF certificates with optional digital signatures via DigiSign
- Data Sovereignty - Self-host on your infrastructure with full control over sensitive employee data
- User and group management with role-based access control (Administrator, Instructor, Member)
- Bulk enrollment of users and groups to courses
- System-wide configuration and monitoring
- OAuth authentication via Google and Microsoft
- Rich text course content editor with multimedia support (images, YouTube/Vimeo videos)
- Flexible test creation with 5 question types:
- Single choice
- Multiple choice
- True/False
- Number input
- Open-ended text
- Automated grading for objective questions
- Manual grading interface for subjective questions
- Course and test analytics
- Browse enrolled courses and learning materials
- Take tests with attempt tracking
- View progress and test history
- Download verified certificates upon course completion
- Modern Architecture - Clean Architecture + Vertical Slice Architecture hybrid
- CQRS Pattern - Separation of command and query responsibilities
- REPR Pattern - Request-Endpoint-Response API design
- OAuth 2.0 - Passwordless authentication via external providers
- JWT Security - Stateless token-based authorization
- Digital Signatures - Integration with DigiSign for eIDAS-compliant certificates
- Domain Events - Event-driven automation for certificate generation
- Automated Audit - Built-in change tracking for all entities
- .NET 9.0 - Primary framework
- ASP.NET Core - Web API with Minimal APIs
- Entity Framework Core - Data access with SQLite (PostgreSQL-ready)
- ASP.NET Core Identity - User and role management
- Wolverine - Mediator and message bus for CQRS
- FluentValidation - Request validation
- QuestPDF - PDF certificate generation
- xUnit - Integration testing
- Vue 3 - Progressive JavaScript framework with Composition API
- TypeScript - Type-safe development
- Pinia - State management
- Vue Router - Client-side routing
- PrimeVue - UI component library
- Axios - HTTP client
- Vite - Build tool and dev server
- Quill.js - Rich text editor
- Docker - Containerization
- SQLite - Default database (development)
- Nginx - Frontend static file serving
- GitLab CI - Continuous integration
For Docker deployment:
- Docker Engine 20.10+
- Docker Compose 2.0+
For development:
- .NET SDK 9.0+
- Node.js 22+ (or Node.js 20.19.0+)
- Git
- Clone the repository
git clone https://gitlab.fit.cvut.cz/nymsaluk/trainingappdockerized.git
cd trainingappdockerized- Start the application
docker-compose up -d- Access the application
- Frontend: http://localhost:3000
- Backend API: http://localhost:5000
- Swagger Documentation: http://localhost:5000/swagger
- First-time setup
- Create an admin user through OAuth login (Google or Microsoft)
- Configure OAuth providers in backend settings (see Configuration)
For detailed deployment instructions, see DEPLOYMENT.md.
- Navigate to backend directory
cd be- Restore dependencies
dotnet restore- Update database
cd TrainingApp.Web
dotnet ef database update- Run the backend
dotnet run --project TrainingApp.WebBackend will be available at http://localhost:5000.
- Navigate to frontend directory
cd fe- Install dependencies
npm install- Configure environment
Create a
.envfile:
VITE_API_BASE_URL=http://localhost:5000
- Run the development server
npm run devFrontend will be available at http://localhost:5173.
TrainingApp implements a hybrid architecture combining Clean Architecture principles with Vertical Slice Architecture:
be/
├── TrainingApp.Core/ # Domain entities and application logic
│ ├── Domain/ # Domain models organized by entity
│ │ ├── Users/
│ │ ├── Courses/
│ │ ├── Tests/
│ │ ├── Enrollments/
│ │ ├── Certificates/
│ │ └── ...
│ └── Application/ # Application layer
│ ├── Features/ # Vertical slices (Commands, Queries, Events)
│ ├── Abstractions/ # Interfaces (IDataAccess, ICqrsDispatcher)
│ └── Common/ # Shared application logic
├── TrainingApp.Infrastructure/ # External concerns
│ ├── Data/ # EF Core DbContext
│ ├── Migrations/ # Database migrations
│ ├── Services/ # DigiSign, Certificate generation
│ ├── Cqrs/ # Wolverine dispatcher adapter
│ ├── Identity/ # ASP.NET Core Identity implementation
│ ├── OAuth/ # OAuth provider integrations
│ └── Persistence/ # Repository implementations
├── TrainingApp.Web/ # API presentation layer
│ ├── Endpoints/ # REPR pattern endpoints
│ ├── Middleware/ # Request logging, error handling
│ └── Pages/ # Razor pages (if any)
└── TrainingApp.Web.IntegrationTests/# Integration test suite
fe/
├── src/
│ ├── components/ # Reusable UI components
│ ├── views/ # Page-level components
│ ├── stores/ # Pinia state management
│ ├── services/ # API client services
│ ├── router/ # Vue Router configuration
│ ├── types/ # TypeScript interfaces
│ └── config/ # App configuration
- Clean Architecture - Dependency inversion with core domain independence
- Vertical Slice Architecture - Feature-based organization within layers
- CQRS - Command Query Responsibility Segregation via Wolverine
- REPR - Request-Endpoint-Response for API design
- Domain Events - Event-driven workflows for certificate automation
- Repository Pattern - Data access abstraction via IDataAccess
The application requires external authentication providers (Google and/or Microsoft) to be configured before first use.
- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Google+ API
- Go to APIs & Services > Credentials and create OAuth 2.0 credentials
- Add authorized redirect URI:
<backend-url>/signin-google(e.g.,http://localhost:5000/signin-google) - Save the Client ID and Client Secret
- Add them to
appsettings.jsonunderAuthentication:Google
- Go to Azure Portal
- Navigate to Azure Active Directory > App registrations
- Register a new application
- Under Authentication, add redirect URI:
<backend-url>/signin-microsoft(e.g.,http://localhost:5000/signin-microsoft) - Go to Certificates & secrets and generate a client secret
- Save the Application (client) ID and the generated secret
- Add them to
appsettings.jsonunderAuthentication:Microsoft
Note: Update redirect URIs if deploying to a different domain or port.
Required only if you want digitally-signed certificates (CertificateSettings:SigningProvider set to DigiSign).
- Go to DigiSign and create an account
- Navigate to Settings and create a new API key
- Configure these scopes:
- Envelopes - read
- Envelopes - edit
- Save the Access Key and Secret Key
- Register a webhook for Envelope completed event with URL:
<backend-url>/api/webhooks/digisign-envelope-completed - Add credentials to
appsettings.jsonunderDigiSign
If you don't need digital signatures, set CertificateSettings:SigningProvider to None and PDFs will be generated without signatures.
Edit be/TrainingApp.Web/appsettings.json and set the keys below. Each section shows a short JSON example followed by a concise explanation.
"ConnectionStrings": {
"DefaultConnection": "Data Source=data/sqlite.db"
}ConnectionStrings:DefaultConnection— EF Core connection string. Default local uses SQLite (data/sqlite.db). For SQL Server/LocalDB or PostgreSQL replace with the provider-specific connection string and ensure the provider packages are installed.
"Jwt": {
"Issuer": "<backend-url>",
"Audience": "<backend-url>",
"Key": "your-secure-key-min-32-characters"
}Jwt:Issuer— issuer claim for JWTs; typically your backend base URL (include scheme,https://...).Jwt:Audience— expected audience claim; commonly the same asIssuerfor this app.Jwt:Key— symmetric signing key for JWTs. Use a cryptographically-secure random string (min 32 chars). Provide via environment variables or a secret manager in production — never commit to source control.
"Authentication": {
"AllowedProviders": ["Google", "Microsoft"],
"Google": { "ClientId": "", "ClientSecret": "" },
"Microsoft": { "ClientId": "", "ClientSecret": "" }
}Authentication:AllowedProviders— array of enabled external providers (Google,Microsoft).Authentication:Google:ClientId/ClientSecret— Google OAuth credentials; set redirect URI to<backend-url>/signin-googlein Google Cloud Console.Authentication:Microsoft:ClientId/ClientSecret— Azure AD credentials; set redirect URI to<backend-url>/signin-microsoftin Azure App Registration.
"Application": {
"SystemUser": { "Email": "system@local" }
},
"DefaultAdmin": { "Email": "admin@local" }Application:SystemUser:Email— optional system/service account email used for automated operations and audit entries.DefaultAdmin:Email— email for the seeded administrator created on first run (idempotent seeding).
"Storage": { "LocalPath": "data/certificates" }Storage:LocalPath— filesystem path (relative to app root) for generated certificates and artifacts. Persist this path in Docker via a volume and ensure write permissions.
"CertificateBranding": {
"CompanyName": "Your Company",
"HeaderText": "CERTIFICATE OF COMPLETION",
"FooterText": "This certificate verifies successful completion of the training course",
"SignatoryName": "",
"SignatoryTitle": ""
},
"CertificateSettings": { "SigningProvider": "None" }CertificateBranding.*— text fields applied to generated PDFs.CertificateSettings:SigningProvider—NoneorDigiSign. IfDigiSignis selected, configureDigiSignvalues below.
"DigiSign": {
"ApiBaseUrl": "https://api.digisign.org",
"AccessKey": "",
"SecretKey": "",
"SignerName": "",
"SignerEmail": "",
"SenderName": "",
"SenderEmail": ""
}DigiSign:*— DigiSign API credentials and signer/sender metadata. KeepAccessKeyandSecretKeysecret and provide via env vars.
"Frontend": {
"AllowedOrigins": ["<frontend-url>"],
"DefaultRedirectUrl": "/auth-callback"
}Frontend:AllowedOrigins— CORS origins for the SPA; include local dev URL(s), production domain(s).Frontend:DefaultRedirectUrl— SPA route that handles OAuth callback redirect.
Setting values via environment variables
- Use environment variables to override
appsettings.json. Replace:with__for Docker/Compose. Examples:ConnectionStrings__ApplicationDbContextConnectionJwt__KeyAuthentication__Google__ClientId
Docker and security notes
- Mount a persistent Docker volume for
Storage:LocalPathand the database file if using SQLite. - Provide secrets (
Jwt:Key, OAuth client secrets, DigiSign keys) through environment variables or a secrets manager; do not commit them to the repo. - Enforce HTTPS and set
Jwt:Issuer/Audienceto the productionhttps://URL.
If you'd like I can generate a commented appsettings.Development.json with placeholders and a docker-compose environment snippet that shows exact env var names. Reply yes to create those files.
Edit fe/.env:
VITE_API_BASE_URL=<backend-url> (e.g., http://localhost:5000)
For production deployment, update docker-compose.yml to set the correct API URL.
See DEPLOYMENT.md for comprehensive deployment guide including:
- Docker Compose setup
- Environment configuration
- Database initialization
- Certificate signing setup
- Production considerations
- Troubleshooting
- Change JWT secret key
- Configure OAuth providers
- Set up HTTPS with reverse proxy
- Configure DigiSign (if using digital signatures)
- Set up automated backups
- Configure logging and monitoring
- Review CORS settings
The backend automatically generates OpenAPI documentation accessible at /swagger/index.html when running in development mode.
- Authentication:
/api/auth/*- OAuth and JWT token management - Users:
/api/users/*- User CRUD operations - Courses:
/api/courses/*- Course management - Tests:
/api/tests/*- Test and question management - Enrollments:
/api/enrollments/*- User enrollment - Attempts:
/api/attempts/*- Test taking and grading - Certificates:
/api/certificates/*- Certificate download - Groups:
/api/groups/*- Group management
All endpoints require JWT authentication except OAuth callbacks and webhooks.
cd be
dotnet test
dotnet formatThe test suite includes:
- API endpoint testing
- Authorization rule validation
- Database operations
- Domain event workflows
Tests use in-memory SQLite database for isolation.
cd fe
npm run lint # ESLint
npm run type-check # TypeScript
npm run format # PrettierThis is an open-source project. Contributions are welcome!
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests and linters
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Merge Request
- Backend: Follow C# coding conventions, use Clean Architecture principles
- Frontend: Use TypeScript, follow Vue 3 Composition API patterns
- Write integration tests for new API endpoints
This project is licensed under the MIT License - see the LICENSE file for details.
Current Version: 1.0.0
- Core LMS functionality
- OAuth authentication (Google, Microsoft)
- Rich text content editor
- Multi-type question support
- Automated and manual grading
- PDF certificate generation
- DigiSign integration for digital signatures
- Docker deployment
- Comprehensive integration tests
For issues, questions, or feature requests, please open an issue on the GitLab repository.
For deployment assistance, see DEPLOYMENT.md.
This software was developed with the support of the Faculty of Information Technology, Czech Technical University in Prague. For more information, visit fit.cvut.cz.


