207 lines
7.3 KiB
C#
207 lines
7.3 KiB
C#
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
|
|
};
|
|
}
|