DeutschLernen/GermanApp/Presentation/Controllers/BootstrapController.cs
Lasse Rune Hansen 50f1b8a8dc feat(backend): Implement mandatory authentication and admin module
- Add Role property to User entity with migration
- Create BootstrapController for first admin user creation
- Remove [AllowAnonymous] from all learning content controllers
- Create AdminController with admin-only endpoints
- Create AdminService for user management
- Create UserReportService for progress reports
- Add UserRepository implementation
- Update AuthService with role support

feat(frontend): Implement authentication system
- Add AuthStore with React context for auth state management
- Create Login, Register, Landing, and Home pages
- Add ProtectedRoute and AdminRoute components
- Create Auth API types and client
- Configure Vite with @/ path alias
- Add comprehensive CSS styles for auth and landing pages

BREAKING CHANGE: All learning content now requires authentication.
Users must register and sign in before accessing lessons, quizzes, and stories.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-14 12:42:15 +02:00

76 lines
2.5 KiB
C#

using GermanApp.Application.DTOs.Auth;
using GermanApp.Application.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace GermanApp.Presentation.Controllers;
/// <summary>
/// Controller for bootstrap operations (first admin user creation).
/// This controller should be removed or disabled after the first admin is created.
/// This is part of the Presentation layer.
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class BootstrapController : ControllerBase
{
private readonly IAuthService _authService;
public BootstrapController(IAuthService authService)
{
_authService = authService;
}
/// <summary>
/// Creates the first admin user.
/// This endpoint is PUBLIC (no authentication required) but can only be used once.
/// After creating the first admin, this endpoint will return 400 BadRequest.
/// </summary>
/// <param name="registerDto">Admin user registration data</param>
/// <returns>Authentication response with JWT token</returns>
[HttpPost("admin")]
[AllowAnonymous]
[ProducesResponseType(typeof(AuthResponse), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.Conflict)]
public async Task<IActionResult> CreateAdminUser([FromBody] RegisterDto registerDto)
{
try
{
var result = await _authService.CreateAdminUserAsync(registerDto);
return Ok(result);
}
catch (InvalidOperationException ex)
{
// Admin already exists - this is expected after first use
if (ex.Message.Contains("Admin user already exists"))
return Conflict(ex.Message);
return BadRequest(ex.Message);
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
}
}
/// <summary>
/// Checks if an admin user already exists.
/// </summary>
/// <returns>True if admin exists, false otherwise</returns>
[HttpGet("admin-exists")]
[AllowAnonymous]
[ProducesResponseType(typeof(bool), (int)HttpStatusCode.OK)]
public async Task<IActionResult> CheckAdminExists()
{
try
{
var exists = await _authService.AdminUserExistsAsync();
return Ok(exists);
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
}
}
}