- 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>
76 lines
2.5 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|