A portfolio-grade collaborative task management application built with ASP.NET Core MVC, ASP.NET Core Web API, ASP.NET Core Identity, EF Core, and PostgreSQL.
The .NET solution and internal project names remain TodoListApp.
- Collaborative mission boards with Owner, Coordinator, and Viewer authority levels.
- Objectives with assignments, due dates, statuses, markers, and comments.
- Command Deck overview of accessible boards, assigned work, and reusable markers.
- Deep Scan search by objective title, creation date, and due date.
- Complete account flows with email confirmation and password recovery.
- Separate MVC WebApp and authenticated WebApi runtime boundary.
Helion Command was developed as my capstone project for the EPAM .NET Development with AI Tools educational program.
The task-requirements directory contains the original assignment materials provided to participants. The application architecture, implementation, automated tests, Helion Command UI concept, and project documentation represent my solution to the assignment.
This repository is an independent portfolio project and is not an official EPAM product.
The application is presented as Helion Command, a serious sci-fi operations interface for managing mission boards, objectives, markers, comments, shared access, and search.
The UI terminology is themed, but backend naming stays requirement-aligned:
| User-facing idea | Backend term or concept |
|---|---|
| Command Deck | Dashboard page |
| Mission Board | TodoList |
| Objective | TodoTask |
| Operations | Tasks page |
| Marker | Tag |
| Comment | Comment |
| Shared access | TodoListMember |
| Authority Level | TodoListAccessLevel |
| Issuer | Owner |
| Coordinator | Editor access level |
| Viewer | Viewer access level |
| Profile / Access Profile | Profile page |
| Deep Scan | Search page |
Backend code, API contracts, database schema, XML documentation, tests, and internal diagnostics use technical requirement-aligned names.
Visible Razor UI copy may use Helion Command terminology.
The required runtime flow is:
Browser → TodoListApp.WebApp → TodoListApp.WebApi → PostgreSQL
TodoListApp.WebApp renders the MVC browser UI and calls TodoListApp.WebApi through HTTP.
TodoListApp.WebApi exposes REST endpoints, validates API requests, authenticates internal Bearer tokens, maps HTTP contracts to Application use cases, and returns HTTP responses.
PostgreSQL stores data in two separate databases:
| Database | Purpose |
|---|---|
users_db |
ASP.NET Core Identity data |
todo_list_db |
Application task data |
The browser authenticates with the WebApp through an Identity cookie.
When the WebApp calls the WebApi, the WebApp server issues a short-lived internal JWT access token and sends it as a Bearer token. The browser does not store internal API access tokens.
src
├── TodoListApp.Domain
├── TodoListApp.Application
├── TodoListApp.Contracts
├── TodoListApp.Persistence
├── TodoListApp.Identity
├── TodoListApp.Infrastructure
├── TodoListApp.WebApi
├── TodoListApp.WebApp
└── TodoListApp.Migrator
tests
├── TodoListApp.Domain.Tests
├── TodoListApp.Application.Tests
├── TodoListApp.Persistence.Tests
├── TodoListApp.Identity.Tests
├── TodoListApp.Infrastructure.Tests
├── TodoListApp.Migrator.Tests
├── TodoListApp.WebApi.IntegrationTests
├── TodoListApp.WebApp.Tests
└── TodoListApp.ArchitectureTests
- .NET 8 SDK
- PostgreSQL
- Docker or Podman for Testcontainers-based tests
- Mailpit or another local SMTP server for local account email testing
justcommand runner, optional but recommended
Restore local .NET tools:
dotnet tool restoreLocal secrets are configured with the .NET Secret Manager.
Secrets must not be committed to source control.
Configure the migrator with both database connection strings:
dotnet user-secrets init --project src/TodoListApp.Migrator/TodoListApp.Migrator.csproj
dotnet user-secrets set \
--project src/TodoListApp.Migrator/TodoListApp.Migrator.csproj \
"ConnectionStrings:UsersDb" \
"Host=localhost;Port=5454;Database=users_db;Username=hc_users_app;Password=<local-users-db-password>"
dotnet user-secrets set \
--project src/TodoListApp.Migrator/TodoListApp.Migrator.csproj \
"ConnectionStrings:TodoListDb" \
"Host=localhost;Port=5454;Database=todo_list_db;Username=hc_todo_app;Password=<local-todo-list-db-password>"Configure the WebApp with the Identity database connection string:
dotnet user-secrets init --project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"ConnectionStrings:UsersDb" \
"Host=localhost;Port=5454;Database=users_db;Username=hc_users_app;Password=<local-users-db-password>"Configure the WebApi with the application database connection string:
dotnet user-secrets init --project src/TodoListApp.WebApi/TodoListApp.WebApi.csproj
dotnet user-secrets set \
--project src/TodoListApp.WebApi/TodoListApp.WebApi.csproj \
"ConnectionStrings:TodoListDb" \
"Host=localhost;Port=5454;Database=todo_list_db;Username=hc_todo_app;Password=<local-todo-list-db-password>"The WebApp and WebApi must use the same internal API token signing key.
Generate a local signing key:
INTERNAL_API_SIGNING_KEY="$(openssl rand -base64 64 | tr -d '\n')"Configure the WebApi:
dotnet user-secrets set \
--project src/TodoListApp.WebApi/TodoListApp.WebApi.csproj \
"InternalApiToken:SigningKey" \
"$INTERNAL_API_SIGNING_KEY"Configure the WebApp:
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"InternalApiToken:SigningKey" \
"$INTERNAL_API_SIGNING_KEY"If the WebApi uses a different local HTTPS port, configure the WebApp base address:
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"WebApi:BaseAddress" \
"https://localhost:<webapi-port>"The WebApp uses SMTP email delivery for email confirmation and password restore.
For local development, Mailpit is used as a fake SMTP server.
Expected local Mailpit endpoints:
| Service | Local address |
|---|---|
| SMTP | localhost:1025 |
| Web UI | http://localhost:3343 |
Configure the WebApp SMTP settings:
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"Email:Smtp:Host" \
"localhost"
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"Email:Smtp:Port" \
"1025"
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"Email:Smtp:SenderAddress" \
"no-reply@helion-command.local"
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"Email:Smtp:SenderName" \
"Helion Command"
dotnet user-secrets set \
--project src/TodoListApp.WebApp/TodoListApp.WebApp.csproj \
"Email:Smtp:UseSsl" \
"false"Mailpit is a local development dependency only. It is not required by the production architecture.
Apply both database schemas through the migrator:
dotnet run --project src/TodoListApp.Migrator/TodoListApp.Migrator.csprojThe migrator applies both users_db and todo_list_db migrations, including Identity schema changes used by email confirmation and resend-confirmation throttling.
Demo data seeding is disabled by default.
To populate a local environment after migrations, enable it for the Migrator and configure the password used when creating missing demo accounts:
dotnet user-secrets set \
--project src/TodoListApp.Migrator/TodoListApp.Migrator.csproj \
"DemoData:Enabled" \
"true"
dotnet user-secrets set \
--project src/TodoListApp.Migrator/TodoListApp.Migrator.csproj \
"DemoData:UserPassword" \
"<local-demo-password>"
dotnet run --project src/TodoListApp.Migrator/TodoListApp.Migrator.csprojThe configured password is stored only in local user secrets and is used when creating missing demo accounts. It must not be committed to source control.
The available demo accounts are:
| Username | Display name | Email confirmed | Intended use |
|---|---|---|---|
aurelia.voss |
Aurelia Voss | Yes | Primary pagination and access flow |
cassian.rook |
Cassian Rook | Yes | Engineering and shared boards |
nyra.quell |
Nyra Quell | Yes | Analysis, search, and markers |
milo.vane |
Milo Vane | Yes | Cartography and board ownership |
juniper.hale |
Juniper Hale | Yes | Safety review and assignments |
orin.calder |
Orin Calder | Yes | Logistics and board ownership |
sable.kestrel |
Sable Kestrel | Yes | Liaison work and shared access |
lumen.pike |
Lumen Pike | No | Email-confirmation flow testing |
Sign in with one of the confirmed usernames and the password configured through DemoData:UserPassword.
lumen.pike uses the same configured password but cannot open an authenticated session until its email is confirmed. This account exists specifically for testing the email-confirmation user experience.
The seeded task-management data includes:
- 15 connected Helion Command incident boards owned by different users;
- shared access with Coordinator and Viewer authority levels;
- 78 objectives in Not Started, In Progress, and Completed states;
- overdue objectives, upcoming objectives, and objectives without due dates;
- assignments distributed across different users;
- 18 board-specific markers and reusable objective-marker links;
- 32 comments written by different users.
Repeated runs are idempotent: the seeder adds only missing seed records. It does not delete unrelated data or overwrite existing account credentials and profile fields.
Existing development databases may retain earlier demo records. The current Helion Command dataset uses isolated deterministic identifier ranges and is added alongside those records. Reset the local databases manually only when a completely fresh copy of the current demo dataset is needed.
The local launch profiles use these development addresses:
| Project | HTTPS | HTTP |
|---|---|---|
| WebApp | https://localhost:7000 |
http://localhost:5000 |
| WebApi | https://localhost:7001 |
http://localhost:5001 |
The WebApp is the browser entry point.
Run both WebApi and WebApp:
just startOpen the WebApp:
https://localhost:7000
just dev is an alias for the same local start workflow:
just devRun the development watch workflow:
just watchThis starts:
- WebApi with
dotnet watch; - WebApp with
dotnet watch; - WebApp Tailwind CSS watch.
Run individual projects when needed:
just run-webapi
just run-webappOpenAPI and Scalar are available from the WebApi in development.
The WebApp uses ASP.NET Core Identity for browser authentication.
Implemented account flows:
- sign up;
- email confirmation;
- resend confirmation email;
- sign in;
- sign out;
- forgot password;
- reset password.
Registration creates an account and sends an email confirmation link. The user is not signed in automatically after registration.
Confirmed email is required before sign-in.
Forgot-password and resend-confirmation pages use generic public responses to avoid account enumeration.
Password reset emails are sent only for confirmed accounts.
Email confirmation and password reset use ASP.NET Core Identity tokens. Tokens are URL-safe encoded for callback links and are not stored in the database.
The user is not signed in automatically after email confirmation or password reset.
Run the main local verification workflow:
just verifyRun formatting only:
just formatRun local SonarQube analysis when needed:
just sonarIf just is not available, run the equivalent commands manually:
dotnet restore
dotnet build
dotnet test
dotnet csharpier format --check .Some integration tests use Testcontainers and require a working Docker-compatible runtime, such as Docker or Podman.
Project documentation is stored in docs.
Start here:
docs/README.mddocs/project-vision.mddocs/architecture/overview.mddocs/architecture/solution-structure.mddocs/architecture/authentication-flow.mddocs/diagrams/solution-dependencies.mddocs/roadmap/future-features.mddocs/ui/app-lore.mddocs/ui/design-direction.mddocs/ui/wireframes.md
Original assignment materials are stored in task-requirements.
Do not modify original requirement text under task-requirements.
The only intended exception is marking completed user stories in task-requirements/README.md, as requested by the assignment backlog instructions.