This commit is contained in:
2020-09-03 17:35:06 +00:00
parent 5825b490fa
commit 3deaa13ce8
4 changed files with 165 additions and 15 deletions

View File

@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using System.Linq;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
@@ -191,7 +192,7 @@ namespace AyaNova.Api.Controllers
/// <param name="reportDataParam">Data required for report</param>
/// <param name="apiVersion">From route path</param>
/// <returns></returns>
[HttpPost("object-report-data")]
[HttpPost("data")]
public async Task<IActionResult> GetReportData([FromBody] ObjectReportDataParameter reportDataParam, ApiVersion apiVersion)
{
/*{
@@ -237,7 +238,7 @@ namespace AyaNova.Api.Controllers
/// </summary>
/// <param name="reportParam">report id and object id values for object type specified in report template</param>
/// <param name="apiVersion">From route path</param>
/// <returns></returns>
/// <returns>downloadable pdf name</returns>
[HttpPost("render")]
public async Task<IActionResult> RenderReport([FromBody] RenderReportParameter reportParam, ApiVersion apiVersion)
{
@@ -253,15 +254,61 @@ namespace AyaNova.Api.Controllers
var API_URL = $"http://127.0.0.1:{httpConnectionFeature.LocalPort}/api/v8/";
var result = await biz.RenderReport(reportParam, API_URL);
if (result == null)
if (string.IsNullOrWhiteSpace(result))
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return new FileContentResult(result.RenderedOutput, result.MimeType);
return Ok(ApiOkResponse.Response(result));
}
/// <summary>
/// Download a report file
/// </summary>
/// <param name="fileName"></param>
/// <param name="t">download token</param>
/// <returns></returns>
[HttpGet("download/{fileName}")]
public async Task<IActionResult> DownloadAsync([FromRoute] string fileName, [FromQuery] string t)
{
int nFailedAuthDelay = 3000;
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (string.IsNullOrWhiteSpace(t))
{
await Task.Delay(nFailedAuthDelay);//DOS protection
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
}
var DownloadUser = await ct.User.AsNoTracking().SingleOrDefaultAsync(z => z.DlKey == t && z.Active == true);
if (DownloadUser == null)
{
await Task.Delay(nFailedAuthDelay);//DOS protection
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
}
var utcNow = new DateTimeOffset(DateTime.Now.ToUniversalTime(), TimeSpan.Zero);
if (DownloadUser.DlKeyExpire < utcNow.DateTime)
{
await Task.Delay(nFailedAuthDelay);//DOS protection
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
}
if (!Authorized.HasModifyRole(DownloadUser.Roles, AyaType.Backup))//not technically modify but treating as such as a backup is very sensitive data
{
await Task.Delay(nFailedAuthDelay);//DOS protection
return StatusCode(403, new ApiNotAuthorizedResponse());
}
if (!FileUtil.BackupFileExists(fileName))
{
await Task.Delay(nFailedAuthDelay);//fishing protection
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
}
string mimetype = fileName.EndsWith("zip") ? "application/zip" : "application/octet-stream";
var utilityFilePath = FileUtil.GetFullPathForBackupFile(fileName);
await EventLogProcessor.LogEventToDatabaseAsync(new Event(DownloadUser.Id, 0, AyaType.NoType, AyaEvent.UtilityFileDownload, fileName), ct);
return PhysicalFile(utilityFilePath, mimetype, fileName);
}
// [HttpGet("render-test")]
// [AllowAnonymous]
@@ -406,7 +453,7 @@ namespace AyaNova.Api.Controllers
}
[HttpPost("post-poc")]
[HttpPost("post-poc")]
public async Task<IActionResult> PostProofOfConcept([FromBody] NameItem nameItem)
{
//https://test.helloayanova.com/api/v8/report/poc

View File

@@ -281,12 +281,8 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//RENDER
//
/*
Both a route (for external calls) that returns a report and an internal biz object that is used by notification for the same purpose
so the route just calls the biz object which handles processing, getting data, checking rights and then making the report and either attaching it to an email (maybe I do need that temp server folder after all)
or return to route to return to Client end
*/
public async Task<RenderedReport> RenderReport(RenderReportParameter reportParam, string apiUrl, AuthorizationRoles overrideRoles = AuthorizationRoles.NoRole)
public async Task<string> RenderReport(RenderReportParameter reportParam, string apiUrl, AuthorizationRoles overrideRoles = AuthorizationRoles.NoRole)
{
var log = AyaNova.Util.ApplicationLogging.CreateLogger("ReportBiz::RenderReport");
@@ -369,15 +365,110 @@ namespace AyaNova.Biz
//If need the generated page content
//var pagecontent = await page.GetContentAsync();
string outputFileName = StringUtil.ReplaceLastOccurrence(FileUtil.NewRandomFileName,".","")+".pdf";
string outputFullPath= System.IO.Path.Combine(FileUtil.TemporaryFilesFolder,outputFileName);
//render to pdf and return
var pdfBuffer = await page.PdfDataAsync();
await page.PdfAsync(outputFullPath);
log.LogDebug($"returning results now:");
return new RenderedReport() { MimeType = "application/pdf", RenderedOutput = pdfBuffer };
return outputFileName;
}
}
// public async Task<RenderedReport> RenderReport(RenderReportParameter reportParam, string apiUrl, AuthorizationRoles overrideRoles = AuthorizationRoles.NoRole)
// {
// var log = AyaNova.Util.ApplicationLogging.CreateLogger("ReportBiz::RenderReport");
// //get report, vet security, see what we need before init in case of issue
// var report = await ct.Report.FirstOrDefaultAsync(z => z.Id == reportParam.ReportId);
// if (report == null)
// {
// AddError(ApiErrorCode.NOT_FOUND, "id");
// return null;
// }
// AuthorizationRoles effectiveRoles = CurrentUserRoles;
// if (overrideRoles != AuthorizationRoles.NoRole)
// effectiveRoles = overrideRoles;
// if (!AyaNova.Api.ControllerHelpers.Authorized.HasReadFullRole(effectiveRoles, report.ObjectType))
// {
// AddError(ApiErrorCode.NOT_AUTHORIZED, null, $"User not authorized for {report.ObjectType} type object");
// return null;
// }
// //Get data
// var ReportData = await GetReportData(new ObjectReportDataParameter() { ObjectType = report.ObjectType, SelectedRowIds = reportParam.SelectedRowIds, DataListKey = reportParam.DataListKey, ListView = reportParam.ListView });
// //initialization
// log.LogDebug("Initializing report system");
// var ReportJSFolderPath = Path.Combine(ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH, "resource", "rpt");
// var lo = new LaunchOptions { Headless = true };
// bool isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);
// if (!isWindows)
// {
// log.LogDebug($"Not Windows: setting executable path for chrome to expected '/usr/bin/chromium-browser'");
// lo.ExecutablePath = "/usr/bin/chromium-browser";//this is the default path for docker based alpine dist, but maybe not others, need to make a config setting likely
// lo.Args = new string[] { "--no-sandbox" };
// }
// else
// {
// log.LogDebug($"Windows: Calling browserFetcher download async now:");
// await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
// }
// log.LogDebug($"Launching headless Chrome now:");
// using (var browser = await Puppeteer.LaunchAsync(lo))
// using (var page = await browser.NewPageAsync())
// {
// log.LogDebug($"Preparing page: adding base reporting scripts to page");
// //Add Handlebars JS for compiling and presenting
// await page.AddScriptTagAsync(new AddTagOptions() { Path = Path.Combine(ReportJSFolderPath, "ay-hb.js") });
// //add Marked for markdown processing
// await page.AddScriptTagAsync(new AddTagOptions() { Path = Path.Combine(ReportJSFolderPath, "ay-md.js") });
// //add stock helpers
// await page.AddScriptTagAsync(new AddTagOptions() { Path = Path.Combine(ReportJSFolderPath, "ay-report.js") });
// //execute to add to handlebars
// await page.EvaluateExpressionAsync("ayRegisterHelpers();");
// log.LogDebug($"Preparing page: adding this report's scripts, style and templates to page");
// //TODO: CUSTOM HELPERS
// //compile and run handlebars template
// var compileScript = $"let reportData=ayPreRender({ReportData});Handlebars.compile({report.Template})(reportData);";
// var resultHTML = await page.EvaluateExpressionAsync<string>(compileScript);
// //render report as HTML
// await page.SetContentAsync(resultHTML);
// //add style (after page or it won't work)
// if (!string.IsNullOrWhiteSpace(report.Style))
// {
// await page.AddStyleTagAsync(new AddTagOptions { Content = report.Style });
// }
// //If need the generated page content
// //var pagecontent = await page.GetContentAsync();
// //render to pdf and return
// var pdfBuffer = await page.PdfDataAsync();
// log.LogDebug($"returning results now:");
// return new RenderedReport() { MimeType = "application/pdf", RenderedOutput = pdfBuffer };
// }
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//JOB / OPERATIONS

View File

@@ -598,7 +598,7 @@ namespace AyaNova.Util
#region General utilities
/// <summary>
/// Get a random file name
/// Get a random file name, no extension
/// </summary>
/// <returns></returns>
internal static string NewRandomFileName

View File

@@ -118,6 +118,18 @@ namespace AyaNova.Util
return str;
}
public static string ReplaceLastOccurrence(string source, string find, string replace)
{
if (source == null) { return source; }
int place = source.LastIndexOf(find);
if (place == -1)
return source;
string result = source.Remove(place, find.Length).Insert(place, replace);
return result;
}
}//eoc