Commit 0e9eff14 authored by Eric Dieckman's avatar Eric Dieckman
Browse files

chore: upgrade to .NET 10, add CanEdit/AppGroups authorization, and fix dev CORS config

parent 7067d145
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -268,3 +268,7 @@ paket-files/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Claude Code
.claude/settings.local.json
.claude/todos/
 No newline at end of file

CLAUDE.md

0 → 100644
+76 −0
Original line number Diff line number Diff line
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Build
dotnet build Cals.Visor.sln

# Run (from src/api)
dotnet run --project src/api/Api.csproj

# Publish (production)
dotnet publish src/api/Api.csproj -c Release -r linux-musl-x64 --self-contained true -p:PublishSingleFile=true

# Docker
docker build -f Dockerfile -t cals-visor:latest .
```

There are no test projects in this solution currently.

StyleCop analyzers run during build (`EnforceCodeStyleInBuild=true` in `Directory.Build.props`). Fix analyzer warnings before committing.

## Architecture

Clean Architecture with 5 projects:

- **`src/api`** — ASP.NET Core 8 Web API. Controllers, authorization handlers/policies, middleware, error handling, and `Program.cs`.
- **`src/application`** — Application layer. Repository interfaces, service interfaces, configuration models, Dapper mappers, and `ServiceRegistration.cs` for DI setup.
- **`src/infrastructure`** — Data access. EF Core `ApplicationDbContext`, entity configurations, repository implementations, `EmailService`, Quartz jobs, and `ServiceRegistration.cs`.
- **`src/models`** — Domain models/entities only. No logic here.
- **`src/migrations`** — Raw SQL migration scripts (not EF migrations). Files are timestamped SQL scripts applied manually.
- **`src/nexus`** — Internal CQRS mediator library (`Cals.Nexus`). Defines `ICommand`, `IQuery`, `ICommandHandler`, `IQueryHandler` interfaces and the mediator implementation.

## Key Patterns

**Authentication:** University of Wisconsin login.wisc.edu (Azure AD OIDC). JWT Bearer tokens. `UserInfoClaimsTransformation` maps OIDC claims to app claims. `VisorUser` wraps `ClaimsPrincipal`.

**Authorization:** Two policies defined in `src/api/Authorization/Policies.cs`:
- `"CanEdit"` — requires membership in the Admins app group
- `"OneAppGroup"` — requires membership in at least one app group

All write endpoints (POST/PUT) require `[Authorize(Policy = Policies.CanEdit)]`.

**Data Access:** Hybrid approach — EF Core (Pomelo MySQL provider) for standard CRUD, Dapper via `IDbConnectionFactoryVisor` for complex queries. DB uses snake_case naming via `EFCore.NamingConventions`.

**Audit Logging:** `AuditSaveChangesInterceptor` auto-tracks changes to entities implementing `IAuditableEntity`. Timestamps (`ICreatedAt`, `IUpdatedAt`) are set automatically.

**Dependency Injection:** Scrutor for assembly scanning. Each layer has a `ServiceRegistration.cs` with an extension method called from `Program.cs`.

**CQRS:** The `Cals.Nexus` mediator is available but not extensively used yet. New feature work can use the command/query pattern via this library.

## Configuration

- Local dev: `appsettings.Development.json` with a local MySQL connection string.
- Production/Staging: Secrets pulled from Azure Key Vault (certificate-based auth). Non-dev environments require `AzureKeyVault__*` env vars and a PFX certificate.
- `AllowedOrigins` in `appsettings.json` controls CORS.
- App group membership (SysAdmin, Admins, Users) configured in `AppGroups.cs` and `appsettings.json`.

## Database Migrations

Migrations are plain SQL files in `src/migrations/scripts/`. To add a migration, create a new timestamped `.sql` file (format: `YYYYMMDDHHMMSS_Description.sql`) and apply it manually to the target database.

## NuGet Sources

Requires access to two internal feeds (configured in `nuget.config`):
- `https://nuget.cals.wisc.edu/api/v2` — CALS packages (`Cals.Authentication.Identity`, `Cals.Cqrs`, `Cals.Data.MySql`)
- `https://wams.doit.wisc.edu:443/nuget/nuget` — WaMS packages

## Commit Convention

Commits must follow Conventional Commits format (enforced by commitlint in CI). Semantic-release uses commits to determine version bumps.

## Related Repos
- SPA: ../spa (Vue 3.5 frontend)
+1 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{918D047A
		.editorconfig = .editorconfig
		.gitattributes = .gitattributes
		.gitignore = .gitignore
		CLAUDE.md = CLAUDE.md
		eng\CodeAnalysis.ruleset = eng\CodeAnalysis.ruleset
		nuget.config = nuget.config
		README.md = README.md
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@
	</PropertyGroup>

	<PropertyGroup>
		<LangVersion>12.0</LangVersion>
		<LangVersion>latest</LangVersion>
	</PropertyGroup>
	
	<PropertyGroup>
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@


# -------- Stage 1: Build .NET app --------
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS dotnet-builder
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS dotnet-builder
WORKDIR /src

# Copy global build files, custom NuGet server and solution
Loading