Initial commit: HelpDesk ICHN - .NET 10 backend + Next.js 16 frontend

This commit is contained in:
Nurfog committed 2026-09-10 10:20:51 -03:00
commit 359f0caf85
90 files changed
+13395

No files matched your search

@@ -0,0 +1,206 @@
using System.Security.Claims;
using HelpDesk.Data.Repositories;
using HelpDesk.Models.DTOs;
using HelpDesk.Models.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace HelpDesk.API.Controllers;
[ApiController]
[Route("api/kb")]
[Authorize]
public class KnowledgeBaseController : ControllerBase
{
private readonly IKnowledgeBaseRepository _kbRepository;
public KnowledgeBaseController(IKnowledgeBaseRepository kbRepository)
{
_kbRepository = kbRepository;
}
// ===== Articles =====
[HttpGet("articles")]
[Authorize(Policy = "Agent")]
public async Task<ActionResult<IEnumerable<ArticleDto>>> GetAllArticles()
{
var articles = await _kbRepository.GetAllArticlesAsync();
return Ok(articles.Select(MapArticleToDto));
}
[HttpGet("articles/published")]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<ArticleDto>>> GetPublishedArticles()
{
var articles = await _kbRepository.GetPublishedArticlesAsync();
return Ok(articles.Select(MapArticleToDto));
}
[HttpGet("search")]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<ArticleDto>>> SearchArticles([FromQuery] string q)
{
if (string.IsNullOrWhiteSpace(q))
return BadRequest(new { error = "Search query is required." });
var articles = await _kbRepository.SearchArticlesAsync(q);
return Ok(articles.Select(MapArticleToDto));
}
[HttpGet("articles/{id}")]
public async Task<ActionResult<ArticleDto>> GetArticle(Guid id)
{
var article = await _kbRepository.GetArticleByIdAsync(id);
if (article == null) return NotFound(new { error = "Article not found." });
return Ok(MapArticleToDto(article));
}
[HttpPost("articles")]
[Authorize(Policy = "Agent")]
public async Task<ActionResult<ArticleDto>> CreateArticle([FromBody] CreateArticleRequest request)
{
if (string.IsNullOrWhiteSpace(request.Title))
return BadRequest(new { error = "Title is required." });
if (string.IsNullOrWhiteSpace(request.Content))
return BadRequest(new { error = "Content is required." });
var userId = GetUserId();
if (userId == null) return Unauthorized();
var article = new KnowledgeBaseArticle
{
Title = request.Title,
Content = request.Content,
CategoryId = request.CategoryId,
IsPublished = request.IsPublished,
AuthorId = userId.Value,
CreatedAt = DateTime.UtcNow
};
var created = await _kbRepository.CreateArticleAsync(article);
var full = await _kbRepository.GetArticleByIdAsync(created.Id);
return CreatedAtAction(nameof(GetArticle), new { id = created.Id }, MapArticleToDto(full!));
}
[HttpPut("articles/{id}")]
[Authorize(Policy = "Agent")]
public async Task<ActionResult<ArticleDto>> UpdateArticle(Guid id, [FromBody] UpdateArticleRequest request)
{
var article = await _kbRepository.GetArticleByIdAsync(id);
if (article == null) return NotFound(new { error = "Article not found." });
if (request.Title != null) article.Title = request.Title;
if (request.Content != null) article.Content = request.Content;
if (request.CategoryId.HasValue) article.CategoryId = request.CategoryId.Value;
if (request.IsPublished.HasValue) article.IsPublished = request.IsPublished.Value;
var updated = await _kbRepository.UpdateArticleAsync(article);
if (updated == null) return StatusCode(500, new { error = "Failed to update article." });
var full = await _kbRepository.GetArticleByIdAsync(id);
return Ok(MapArticleToDto(full!));
}
[HttpDelete("articles/{id}")]
[Authorize(Policy = "Admin")]
public async Task<IActionResult> DeleteArticle(Guid id)
{
var deleted = await _kbRepository.DeleteArticleAsync(id);
if (!deleted) return NotFound(new { error = "Article not found." });
return NoContent();
}
// ===== Categories =====
[HttpGet("categories")]
public async Task<ActionResult<IEnumerable<CategoryDto>>> GetCategories()
{
var categories = await _kbRepository.GetAllCategoriesAsync();
return Ok(categories.Select(MapCategoryToDto));
}
[HttpGet("categories/{id}")]
public async Task<ActionResult<CategoryDto>> GetCategory(Guid id)
{
var category = await _kbRepository.GetCategoryByIdAsync(id);
if (category == null) return NotFound(new { error = "Category not found." });
return Ok(MapCategoryToDto(category));
}
[HttpPost("categories")]
[Authorize(Policy = "Admin")]
public async Task<ActionResult<CategoryDto>> CreateCategory([FromBody] CreateCategoryRequest request)
{
if (string.IsNullOrWhiteSpace(request.Name))
return BadRequest(new { error = "Name is required." });
var category = new KnowledgeBaseCategory
{
Name = request.Name,
Description = request.Description,
SortOrder = request.SortOrder,
CreatedAt = DateTime.UtcNow
};
var created = await _kbRepository.CreateCategoryAsync(category);
return CreatedAtAction(nameof(GetCategory), new { id = created.Id }, MapCategoryToDto(created));
}
[HttpPut("categories/{id}")]
[Authorize(Policy = "Admin")]
public async Task<ActionResult<CategoryDto>> UpdateCategory(Guid id, [FromBody] CreateCategoryRequest request)
{
var category = await _kbRepository.GetCategoryByIdAsync(id);
if (category == null) return NotFound(new { error = "Category not found." });
category.Name = request.Name;
category.Description = request.Description;
category.SortOrder = request.SortOrder;
var updated = await _kbRepository.UpdateCategoryAsync(category);
if (updated == null) return StatusCode(500, new { error = "Failed to update category." });
return Ok(MapCategoryToDto(updated));
}
[HttpDelete("categories/{id}")]
[Authorize(Policy = "Admin")]
public async Task<IActionResult> DeleteCategory(Guid id)
{
var deleted = await _kbRepository.DeleteCategoryAsync(id);
if (!deleted) return NotFound(new { error = "Category not found." });
return NoContent();
}
private Guid? GetUserId()
{
var claim = User.FindFirst("userId") ?? User.FindFirst(ClaimTypes.NameIdentifier);
return claim != null && Guid.TryParse(claim.Value, out var id) ? id : null;
}
private static ArticleDto MapArticleToDto(KnowledgeBaseArticle a) => new()
{
Id = a.Id,
Title = a.Title,
Content = a.Content,
IsPublished = a.IsPublished,
CategoryId = a.CategoryId,
CategoryName = a.Category?.Name ?? "Unknown",
AuthorId = a.AuthorId,
AuthorName = a.Author != null ? $"{a.Author.FirstName} {a.Author.LastName}" : "Unknown",
ViewCount = a.ViewCount,
CreatedAt = a.CreatedAt,
UpdatedAt = a.UpdatedAt
};
private static CategoryDto MapCategoryToDto(KnowledgeBaseCategory c) => new()
{
Id = c.Id,
Name = c.Name,
Description = c.Description,
SortOrder = c.SortOrder,
ArticleCount = c.Articles?.Count ?? 0,
CreatedAt = c.CreatedAt
};
}