refactor: Clean Architecture mit Repository Pattern, Controllern und DTOs
CI - Build & Test / Backend (.NET) (push) Successful in 54s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 19s
CI - Build & Test / Security Check (push) Successful in 2s

- 15 Controller-Klassen ersetzen Minimal APIs in Program.cs
- Repository Pattern mit Interfaces + Implementierungen (Project, Task, Activity, User)
- AuthService verwendet jetzt IUserRepository statt direktem DbContext-Zugriff
- SecurityHeadersMiddleware als eigenständige Middleware-Klasse
- PathSecurityHelper als gemeinsamer Helper für Pfadvalidierung
- DTOs in eigenem Namespace Nexus.Api.DTOs
- EF-Entities in Nexus.Api.Data (vorher Nexus.Api.Domain)
- Program.cs auf DI-Registrierung + Middleware reduziert
- Alle 43 Endpoints unverändert erhalten
- Build + 3/3 Tests erfolgreich
This commit is contained in:
2026-06-09 19:52:58 +02:00
parent 13d4c2f157
commit a79d8282dc
45 changed files with 1590 additions and 1182 deletions
+59
View File
@@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore;
using Nexus.Api.Data;
namespace Nexus.Api.Repositories;
public sealed class UserRepository(NexusDbContext db) : IUserRepository
{
public ValueTask<NexusUser?> GetByIdAsync(Guid userId, CancellationToken ct = default)
=> db.Users.FindAsync([userId], ct);
public Task<NexusUser?> GetByEmailAsync(string normalizedEmail, CancellationToken ct = default)
=> db.Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, ct);
public Task<bool> AnyUsersAsync(CancellationToken ct = default)
=> db.Users.AnyAsync(ct);
public async Task<NexusUser> AddAsync(NexusUser user, CancellationToken ct = default)
{
db.Users.Add(user);
await db.SaveChangesAsync(ct);
return user;
}
public Task UpdateAsync(NexusUser user, CancellationToken ct = default)
=> db.SaveChangesAsync(ct);
public Task<RefreshToken?> GetRefreshTokenByHashAsync(string tokenHash, CancellationToken ct = default)
=> db.RefreshTokens
.Include(r => r.User)
.FirstOrDefaultAsync(r => r.TokenHash == tokenHash, ct);
public Task<List<RefreshToken>> GetActiveTokensByFamilyAsync(Guid familyId, CancellationToken ct = default)
=> db.RefreshTokens
.Where(r => r.FamilyId == familyId && r.RevokedAt == null)
.ToListAsync(ct);
public async Task AddRefreshTokenAsync(RefreshToken token, CancellationToken ct = default)
{
db.RefreshTokens.Add(token);
await db.SaveChangesAsync(ct);
}
public Task UpdateRefreshTokenAsync(RefreshToken token, CancellationToken ct = default)
=> db.SaveChangesAsync(ct);
public async Task RemoveExpiredTokensAsync(Guid userId, CancellationToken ct = default)
{
var cutoff = DateTimeOffset.UtcNow.AddDays(-30);
var oldTokens = await db.RefreshTokens
.Where(r => r.UserId == userId && (r.ExpiresAt < DateTimeOffset.UtcNow || r.RevokedAt < cutoff))
.ToListAsync(ct);
if (oldTokens.Count > 0)
db.RefreshTokens.RemoveRange(oldTokens);
}
public Task SaveChangesAsync(CancellationToken ct = default)
=> db.SaveChangesAsync(ct);
}