397 lines
17 KiB
C#
397 lines
17 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using System.Collections.Generic;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Oqtane.Shared;
|
|
using Oqtane.Enums;
|
|
using Oqtane.Infrastructure;
|
|
using SZUAbsolventenverein.Module.PremiumArea.Services;
|
|
using Oqtane.Controllers;
|
|
using System.Net;
|
|
using System.Threading.Tasks;
|
|
using SZUAbsolventenverein.Module.PremiumArea.Models;
|
|
using System.IO;
|
|
using System;
|
|
using Oqtane.Models;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Oqtane.Repository;
|
|
using Oqtane.Security;
|
|
using System.Linq;
|
|
using Oqtane.Managers;
|
|
using SZUAbsolventenverein.Module.PremiumArea.Repository;
|
|
|
|
namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
|
{
|
|
[Route(ControllerRoutes.ApiRoute)]
|
|
public class EngineerApplicationController : ModuleControllerBase
|
|
{
|
|
private readonly IEngineerApplicationService _service;
|
|
private readonly IFileRepository _files;
|
|
private readonly IFolderRepository _folders;
|
|
private readonly IUserManager _users;
|
|
private readonly IUserPremiumRepository _premiums;
|
|
private readonly IHttpContextAccessor _accessor;
|
|
private readonly IWebHostEnvironment _environment;
|
|
|
|
public EngineerApplicationController(IEngineerApplicationService service, IFileRepository files,
|
|
IFolderRepository folders, IUserManager users, IUserPremiumRepository premiums, ILogManager logger,
|
|
IHttpContextAccessor accessor, IWebHostEnvironment environment) : base(logger, accessor)
|
|
{
|
|
_service = service;
|
|
_files = files;
|
|
_folders = folders;
|
|
_users = users;
|
|
_premiums = premiums;
|
|
_accessor = accessor;
|
|
_environment = environment;
|
|
}
|
|
|
|
// GET: api/<controller>?moduleid=x
|
|
[HttpGet]
|
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
|
public async Task<IEnumerable<EngineerApplication>> Get(string moduleid)
|
|
{
|
|
int ModuleId;
|
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
|
{
|
|
return await _service.GetApplicationsAsync(ModuleId);
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
|
"Unauthorized EngineerApplication Get Attempt {ModuleId}", moduleid);
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// GET: api/<controller>/status/Approved?moduleid=x
|
|
[HttpGet("status/{status}")]
|
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
|
public async Task<IEnumerable<EngineerApplication>> GetByStatus(string status, string moduleid)
|
|
{
|
|
int ModuleId;
|
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
|
{
|
|
return await _service.GetApplicationsAsync(ModuleId, status);
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
|
"Unauthorized EngineerApplication GetByStatus Attempt {ModuleId}", moduleid);
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// GET api/<controller>/5
|
|
[HttpGet("{id}")]
|
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
|
public async Task<EngineerApplication> Get(int id, string moduleid)
|
|
{
|
|
int ModuleId;
|
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
|
{
|
|
return await _service.GetApplicationAsync(id, ModuleId);
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
|
"Unauthorized EngineerApplication Get Attempt {Id} {ModuleId}", id, moduleid);
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// POST api/<controller>
|
|
[HttpPost]
|
|
[Authorize(Policy = PolicyNames.ViewModule)] // Users can Create
|
|
public async Task<EngineerApplication> Post([FromBody] EngineerApplication Application)
|
|
{
|
|
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId))
|
|
{
|
|
try
|
|
{
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Create,
|
|
"DEBUG: Attempting to save application. UserId: {UserId}, ModuleId: {ModuleId}, FileId: {FileId}",
|
|
Application.UserId, Application.ModuleId, Application.FileId);
|
|
|
|
// Manual validation before EF Core sees it
|
|
if (Application.UserId == 0)
|
|
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "DEBUG: UserId is 0!");
|
|
if (Application.FileId == null || Application.FileId == 0)
|
|
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "DEBUG: FileId is null or 0!");
|
|
|
|
var result = await _service.AddApplicationAsync(Application);
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "DEBUG: Save successful!");
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var innerMessage = ex.InnerException?.Message ?? "No inner exception";
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
|
"CRITICAL DB ERROR: {Message}. Inner: {Inner}", ex.Message, innerMessage);
|
|
|
|
// Force output to console so the user sees it immediately
|
|
Console.WriteLine("========================================");
|
|
Console.WriteLine($"!!! DATABASE INSERT FAILED !!!");
|
|
Console.WriteLine($"Error: {ex.Message}");
|
|
Console.WriteLine($"Inner: {innerMessage}");
|
|
Console.WriteLine($"Stack: {ex.StackTrace}");
|
|
Console.WriteLine("========================================");
|
|
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
|
"Unauthorized EngineerApplication Post Attempt {Application}", Application);
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// PUT api/<controller>/5
|
|
[HttpPut("{id}")]
|
|
[Authorize(Policy = PolicyNames.ViewModule)] // Users can Edit own (Service checks ownership)
|
|
public async Task<EngineerApplication> Put(int id, [FromBody] EngineerApplication Application)
|
|
{
|
|
if (ModelState.IsValid && Application.ApplicationId == id &&
|
|
IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId))
|
|
{
|
|
try
|
|
{
|
|
return await _service.UpdateApplicationAsync(Application);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Update, ex,
|
|
"Error updating application: {Message}. Inner: {Inner}", ex.Message,
|
|
ex.InnerException?.Message);
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
|
"Unauthorized EngineerApplication Put Attempt {Application}", Application);
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// DELETE api/<controller>/5
|
|
[HttpDelete("{id}")]
|
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
|
public async Task Delete(int id, string moduleid)
|
|
{
|
|
int ModuleId;
|
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
|
{
|
|
await _service.DeleteApplicationAsync(id, ModuleId);
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
|
"Unauthorized EngineerApplication Delete Attempt {Id} {ModuleId}", id, moduleid);
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
}
|
|
}
|
|
|
|
[HttpPost("approve/{id}")]
|
|
[Authorize(Policy = PolicyNames.EditModule)]
|
|
public async Task Approve(int id, string moduleid)
|
|
{
|
|
int ModuleId;
|
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
|
{
|
|
await _service.ApproveApplicationAsync(id, ModuleId);
|
|
}
|
|
else
|
|
{
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
}
|
|
}
|
|
|
|
[HttpGet("log")]
|
|
[AllowAnonymous]
|
|
public IActionResult GetLog()
|
|
{
|
|
try
|
|
{
|
|
var repo = (EngineerApplicationRepository)_service.GetType().GetField("_repository",
|
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
|
.GetValue(_service);
|
|
var factory = (IDbContextFactory<PremiumAreaContext>)repo.GetType().GetField("_factory",
|
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(repo);
|
|
using var db = factory.CreateDbContext();
|
|
|
|
var connection = db.Database.GetDbConnection();
|
|
connection.Open();
|
|
using var command = connection.CreateCommand();
|
|
command.CommandText = "PRAGMA table_info(SZUAbsolventenvereinEngineerApplications);";
|
|
var columns = new List<string>();
|
|
using var reader = command.ExecuteReader();
|
|
while (reader.Read())
|
|
{
|
|
columns.Add($"{reader["name"]} ({reader["type"]})");
|
|
}
|
|
|
|
if (columns.Count == 0)
|
|
{
|
|
// Maybe it's rewritten?
|
|
command.CommandText =
|
|
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%Engineer%';";
|
|
using var reader2 = command.ExecuteReader();
|
|
while (reader2.Read())
|
|
{
|
|
columns.Add($"Found table: {reader2["name"]}");
|
|
}
|
|
}
|
|
|
|
return Ok(columns);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
|
"Error getting columns: {Message}. Inner: {Inner}", ex.Message, ex.InnerException?.Message);
|
|
return Ok($"Error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
[HttpPost("upload")]
|
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
|
public async Task<IActionResult> Upload(string moduleid)
|
|
{
|
|
int ModuleId;
|
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
|
{
|
|
try
|
|
{
|
|
if (Request.Form.Files.Count == 0) return BadRequest("No file uploaded");
|
|
|
|
var file = Request.Form.Files[0];
|
|
if (file.ContentType != "application/pdf") return BadRequest("Only PDF files are allowed");
|
|
|
|
var alias = _accessor.HttpContext.Items["Alias"] as Alias; // Retained original alias retrieval
|
|
var siteId = alias.SiteId;
|
|
var folderPath = "EngineerApplications";
|
|
|
|
var folder = _folders.GetFolder(siteId, folderPath);
|
|
|
|
if (folder == null)
|
|
{
|
|
try
|
|
{
|
|
// Create folder
|
|
folder = new Folder
|
|
{
|
|
SiteId = siteId,
|
|
ParentId = null,
|
|
Name = "EngineerApplications",
|
|
Path = folderPath,
|
|
PermissionList = new List<Permission>
|
|
{
|
|
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
|
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
|
}
|
|
};
|
|
folder = _folders.AddFolder(folder);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
|
"Error creating folder: {Message}", ex.Message);
|
|
return BadRequest($"Folder creation failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
var ext = Path.GetExtension(file.FileName).ToLower();
|
|
if (ext != ".pdf") return BadRequest("Invalid file extension");
|
|
|
|
var tenantId = alias.TenantId;
|
|
var uploadPath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants",
|
|
tenantId.ToString(), "Sites", siteId.ToString(), folderPath);
|
|
if (!Directory.Exists(uploadPath)) Directory.CreateDirectory(uploadPath);
|
|
|
|
var uniqueName = $"{Guid.NewGuid()}{ext}";
|
|
var filePath = Path.Combine(uploadPath, uniqueName);
|
|
|
|
using (var stream = new FileStream(filePath, FileMode.Create))
|
|
{
|
|
await file.CopyToAsync(stream);
|
|
}
|
|
|
|
var fileObj = new Oqtane.Models.File
|
|
{
|
|
FolderId = folder.FolderId,
|
|
Name = uniqueName,
|
|
Extension = ext.Substring(1),
|
|
Size = (int)file.Length,
|
|
ImageHeight = 0,
|
|
ImageWidth = 0
|
|
};
|
|
|
|
try
|
|
{
|
|
var addedFile = _files.AddFile(fileObj);
|
|
return Ok(new { FileId = addedFile.FileId, FileName = file.FileName });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
|
"Error saving file record to DB: {Message}. Inner: {Inner}", ex.Message,
|
|
ex.InnerException?.Message);
|
|
// Critical: This is where we suspect the DbUpdateException
|
|
Console.WriteLine($"UPLOAD DB ERROR: {ex.Message} | {ex.InnerException?.Message}");
|
|
return BadRequest($"Database error during file registration: {ex.Message}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "General Upload Error: {Message}",
|
|
ex.Message);
|
|
return BadRequest(ex.Message);
|
|
}
|
|
}
|
|
|
|
return StatusCode((int)HttpStatusCode.Forbidden);
|
|
}
|
|
|
|
[HttpPost("report/{id}")]
|
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
|
public async Task Report(int id, [FromBody] string reason, string moduleid)
|
|
{
|
|
int ModuleId;
|
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
|
{
|
|
await _service.ReportApplicationAsync(id, ModuleId, reason);
|
|
}
|
|
else
|
|
{
|
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
}
|
|
}
|
|
|
|
private async Task<IActionResult> ServeFile(int fileId, string downloadName)
|
|
{
|
|
var file = _files.GetFile(fileId);
|
|
if (file != null)
|
|
{
|
|
var path = _files.GetFilePath(file);
|
|
if (System.IO.File.Exists(path))
|
|
{
|
|
var bytes = await System.IO.File.ReadAllBytesAsync(path);
|
|
return File(bytes, "application/pdf", downloadName ?? file.Name);
|
|
}
|
|
}
|
|
|
|
return NotFound();
|
|
}
|
|
}
|
|
}
|