DB Migrtation geändert und PDF upload funktioniert
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -33,7 +34,9 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
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)
|
||||
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;
|
||||
@@ -56,7 +59,8 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Get Attempt {ModuleId}", moduleid);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Get Attempt {ModuleId}", moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -72,9 +76,10 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
{
|
||||
return await _service.GetApplicationsAsync(ModuleId, status);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication GetByStatus Attempt {ModuleId}", moduleid);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication GetByStatus Attempt {ModuleId}", moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -85,17 +90,18 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
[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;
|
||||
}
|
||||
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>
|
||||
@@ -105,11 +111,44 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
{
|
||||
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId))
|
||||
{
|
||||
return await _service.AddApplicationAsync(Application);
|
||||
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);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Post Attempt {Application}", Application);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -120,13 +159,26 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
[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))
|
||||
if (ModelState.IsValid && Application.ApplicationId == id &&
|
||||
IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId))
|
||||
{
|
||||
return await _service.UpdateApplicationAsync(Application);
|
||||
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);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Put Attempt {Application}", Application);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -144,12 +196,12 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Delete Attempt {Id} {ModuleId}", id, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Delete Attempt {Id} {ModuleId}", id, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
// POST api/<controller>/approve/5
|
||||
[HttpPost("approve/{id}")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public async Task Approve(int id, string moduleid)
|
||||
@@ -157,16 +209,57 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
// We need to clear IsReported flag as well.
|
||||
// Since the Service handles the logic, we should probably update it there.
|
||||
// But if I can't find it easily, I can do it here if I get the app first.
|
||||
// _service.ApproveApplicationAsync might just set Status="Approved".
|
||||
// I should verify where the service logic is.
|
||||
await _service.ApproveApplicationAsync(id, ModuleId);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,63 +270,95 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
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;
|
||||
var siteId = alias.SiteId;
|
||||
var folderPath = "EngineerApplications";
|
||||
var folder = _folders.GetFolder(siteId, folderPath);
|
||||
|
||||
if (folder == null)
|
||||
try
|
||||
{
|
||||
// Create folder
|
||||
folder = new Folder
|
||||
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)
|
||||
{
|
||||
SiteId = siteId,
|
||||
ParentId = null,
|
||||
Name = "EngineerApplications",
|
||||
Path = folderPath,
|
||||
PermissionList = new List<Permission>
|
||||
try
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
// 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
|
||||
};
|
||||
folder = _folders.AddFolder(folder);
|
||||
}
|
||||
|
||||
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);
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
var fileObj = new Oqtane.Models.File
|
||||
catch (Exception ex)
|
||||
{
|
||||
FolderId = folder.FolderId,
|
||||
Name = uniqueName,
|
||||
Extension = ext.Substring(1),
|
||||
Size = (int)file.Length,
|
||||
ImageHeight = 0,
|
||||
ImageWidth = 0
|
||||
};
|
||||
|
||||
var addedFile = _files.AddFile(fileObj);
|
||||
|
||||
return Ok(new { FileId = addedFile.FileId, FileName = file.FileName });
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "General Upload Error: {Message}",
|
||||
ex.Message);
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode((int)HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
@@ -246,71 +371,25 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
{
|
||||
await _service.ReportApplicationAsync(id, ModuleId, reason);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("download/{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Download(int id, string moduleid)
|
||||
{
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
var app = await _service.GetApplicationAsync(id, ModuleId);
|
||||
if (app == null) return NotFound();
|
||||
|
||||
var alias = _accessor.HttpContext.Items["Alias"] as Alias;
|
||||
// Access Rules:
|
||||
// 1. Admin
|
||||
if (_accessor.HttpContext.User.IsInRole(RoleNames.Admin))
|
||||
{
|
||||
return await ServeFile(app.FileId.Value, app.PdfFileName);
|
||||
}
|
||||
|
||||
// 2. Owner
|
||||
var username = _accessor.HttpContext.User.Identity.Name;
|
||||
var currentUserId = -1;
|
||||
if (username != null)
|
||||
{
|
||||
var u = _users.GetUser(username, alias.SiteId);
|
||||
if (u != null) currentUserId = u.UserId;
|
||||
}
|
||||
|
||||
if (currentUserId == app.UserId)
|
||||
{
|
||||
return await ServeFile(app.FileId.Value, app.PdfFileName);
|
||||
}
|
||||
|
||||
// 3. Premium User AND Published/Approved
|
||||
if (app.Status == "Approved" || app.Status == "Published")
|
||||
{
|
||||
var premium = _premiums.GetUserPremium(currentUserId);
|
||||
if (premium != null && premium.PremiumUntil.HasValue && premium.PremiumUntil.Value > DateTime.UtcNow)
|
||||
{
|
||||
return await ServeFile(app.FileId.Value, app.PdfFileName);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode((int)HttpStatusCode.Forbidden);
|
||||
}
|
||||
return 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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user