Skip to content

Sebastians-codes/TodosApi

Repository files navigation

TodosApi

TodosApi is a collaborative task and note management backend built with ASP.NET Core 9 minimal APIs. It provides JWT-secured REST endpoints for managing users, todos, notes, and friendships together with real-time notifications delivered over WebSockets. The solution is split into API, Core domain, Infrastructure, and automated test projects to keep concerns isolated and maintainable.

Highlights

  • Token-based authentication using JWT with refresh-free access tokens.
  • Todo and note CRUD with fine-grained access control (owner, collaborators, friends).
  • Friendship workflows (requests, accept/decline, blocking) that control sharing shortcuts.
  • WebSocket hub (/ws) that streams binary update signals to connected clients and handles ping/pong heartbeats.
  • Centralised exception handling and validation helpers that translate domain errors into consistent API responses.
  • SQLite persistence by default with Entity Framework Core migrations; in-memory database automatically used during tests.
  • End-to-end, integration, and unit testing coverage using xUnit, WebApplicationFactory, Moq, and EF Core InMemory.
  • Dockerfile and docker-compose.yaml for containerised deployment behind Nginx, plus helper scripts for deployment and smoke tests.

Solution Layout

Path Description
Api/ ASP.NET Core minimal API surface (endpoints, DTOs, middleware, WebSocket hub, services).
Core/ Domain models, enums, interfaces, and settings shared across the solution.
Infrastructure/ Entity Framework Core context, migrations, repository implementations, and data access.
TodosApi.Tests/ xUnit integration and unit tests targeting the API and domain logic.
docker-compose.yaml, DockerFile Container build and runtime definitions (API + Nginx reverse proxy).
deploy.sh, end-to-end-test.sh Convenience scripts for VPS deployment and full happy-path smoke testing.

Tech Stack

  • .NET SDK 9.0
  • ASP.NET Core minimal APIs & middleware
  • Entity Framework Core 9 (SQLite + InMemory providers)
  • JWT (Microsoft.IdentityModel.Tokens)
  • WebSockets
  • Swashbuckle / OpenAPI for interactive docs
  • xUnit + WebApplicationFactory + Moq for automated tests

Prerequisites

  • .NET 9 SDK
  • SQLite 3 (CLI optional but useful for inspecting todos.db)
  • (Optional) Docker Engine + Docker Compose v2 for container workflows

Validate the SDK globally:

dotnet --list-sdks

Ensure 9.0.x is listed.

Local Development

  1. Restore dependencies:
    dotnet restore Api/Api.csproj
  2. Update configuration as needed. The development settings live in Api/appsettings.Development.json. At minimum replace the placeholder JwtSettings:SecretKey with a 32+ character string.
  3. Apply database migrations (creates Api/todos.db):
    dotnet ef database update \
      --project Infrastructure/Infrastructure.csproj \
      --startup-project Api/Api.csproj

    The command requires the dotnet-ef CLI tool; install it once with dotnet tool install --global dotnet-ef.

  4. Run the API:
    dotnet run --project Api/Api.csproj
    The app listens on the default Kestrel ports (an HTTPS + HTTP pair printed in the console). Swagger UI is available at /swagger when ASPNETCORE_ENVIRONMENT=Development.
  5. Connect a WebSocket client at ws://localhost:<http-port>/ws?token=<jwt> to receive realtime update notifications.

Common Configuration Keys

Key Purpose
ConnectionStrings:Sqlite Path to the SQLite database file. The production profile points to /app/todos.db.
JwtSettings:SecretKey HMAC secret used to sign tokens. Replace the placeholder before deploying.
JwtSettings:Issuer, Audience, ExpirationMinutes JWT validation parameters for API and clients.
UseTesting When true (or when ASPNETCORE_ENVIRONMENT=Testing) the API switches EF Core to the in-memory provider, used by automated tests.
TestDatabaseName Optional override for the in-memory database name during tests.

All keys can be supplied via environment variables using the double-underscore (__) notation (e.g., JwtSettings__SecretKey).

Automated Tests

Run the full test suite (unit + integration):

dotnet test
  • Integration tests spin up an in-memory version of the API using WebApplicationFactory and set UseTesting=true to avoid touching your local SQLite file.
  • Unit tests cover domain rules and repository behaviours with in-memory dependencies.

For a scripted end-to-end sanity check against a running local instance (expected at http://localhost:5126), run:

./end-to-end-test.sh

The script walks through registration, login, todo/note CRUD, sharing, friendships, and authentication edge cases. Update API_BASE inside the script if your server listens on a different host/port.

Running with Docker

Build and start the stack (API + Nginx reverse proxy):

docker compose up --build -d

The compose file exposes the API through Nginx on ports 80 and 443, and maps Api/appsettings.Production.json into the container. Update that file with production secrets before building. The SQLite database file persists via the bind-mounted Api/ folder.

The helper script ./deploy.sh wraps Docker installation checks, secret generation, rebuild, and container startup - handy for provisioning a fresh Linux server.

Realtime Notifications

  • WebSocket endpoint: ws(s)://<host>/ws
  • Authentication: supply the JWT either in the Authorization header during the HTTP handshake or as the token query string (the server explicitly looks at the query parameter for WebSocket clients).
  • Messages: binary payloads containing an UpdateType byte (see Core/DomainObjects/UpdateType.cs). Clients should translate the enum to decide which data needs refreshing; the API doesn't push full resource payloads over the socket.
  • Keep-alive: send "ping" text frames periodically; the hub replies with "pong" and tracks connection health for cleanup.

API Surface (summary)

Area Endpoints
Auth & Users POST /api/users/register, POST /api/users/login, GET /api/users/{id}, PUT /api/users/{id}, POST /api/users/{id}/change-password, GET /api/users/search/{term}
Todos GET /api/todos/user, POST /api/todos, PUT /api/todos/{todoId}, DELETE /api/todos/{todoId}, POST /api/todos/{todoId}/complete, POST /api/todos/{todoId}/access, POST /api/todos/{todoId}/share-with-friends
Notes GET /api/notes/user, GET /api/notes/{noteId}, POST /api/notes, PUT /api/notes/{noteId}, DELETE /api/notes/{noteId}, POST /api/notes/{noteId}/access, POST /api/notes/{noteId}/share-with-friends
Friendships POST /api/friendships/send, POST /api/friendships/accept, POST /api/friendships/decline, POST /api/friendships/block/{targetUserId}, GET /api/friendships/pending, GET /api/friendships/friends, GET /api/friendships/check/{userId1}/{userId2}, DELETE /api/friendships/{friendId}
WebSockets GET /ws?token=<jwt> (upgrade to WebSocket)

All authenticated endpoints expect a Bearer <token> header. Validation errors (400), authorization failures (401/403), and domain errors share a consistent ApiResponse payload shape defined under Api/Models.

Happy hacking!

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published