diff --git a/Projects/DLL/SystemX.Core.dll b/Projects/DLL/SystemX.Core.dll index 637fb5e..b057bf5 100644 Binary files a/Projects/DLL/SystemX.Core.dll and b/Projects/DLL/SystemX.Core.dll differ diff --git a/Projects/SystemX.Core/SystemX.Core/Communication/Http.cs b/Projects/SystemX.Core/SystemX.Core/Communication/Http.cs index 4bd12ad..9c92073 100644 --- a/Projects/SystemX.Core/SystemX.Core/Communication/Http.cs +++ b/Projects/SystemX.Core/SystemX.Core/Communication/Http.cs @@ -95,6 +95,40 @@ namespace SystemX.Core.Communication return response; } + public virtual async Task GetStreamAsync(string url, string bearerToken = "", short timeOutSeconds = 10) + { + Guid guid = Guid.NewGuid(); + + Stream stream = null; + + using (HttpClient httpClient = new HttpClient(GetClientHandler())) + { + try + { + var timeOutSec = SetTimeout(timeOutSeconds); + httpClient.Timeout = new TimeSpan(0, 0, timeOutSec); + httpClient.BaseAddress = new Uri($"{url}"); + + if (string.IsNullOrEmpty(bearerToken) == false) + httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $"{bearerToken}"); + + Log4net.WriteLine($"[GET] Request({guid})::{url}", LogType.HTTP); + + DateTime requestTime = DateTime.Now; + var response = await httpClient.GetAsync(url); + stream = await response.Content.ReadAsStreamAsync(); + + Log4net.WriteLine($"[GET] Rseponse({guid}) ({(DateTime.Now - requestTime).TotalSeconds} sec)::{url}", LogType.HTTP); + } + catch (Exception e) + { + Log4net.WriteLine(e); + } + } + + return stream; + } + protected HttpClientHandler GetClientHandler() { diff --git a/Projects/VPKI/VPKI/VPKI.Library/Model/CertificateHistoryModel.cs b/Projects/VPKI/VPKI/VPKI.Library/Model/CertificateHistoryModel.cs index 133e478..fdced43 100644 --- a/Projects/VPKI/VPKI/VPKI.Library/Model/CertificateHistoryModel.cs +++ b/Projects/VPKI/VPKI/VPKI.Library/Model/CertificateHistoryModel.cs @@ -14,5 +14,7 @@ namespace VPKI.Library.Model public TCertificate? TCertificate { get; set; } public TVerifyResult? TVerifyResult { get; set; } public TOcsp? TOcsp { get; set; } + + public int TotalCount { get; set; } } } diff --git a/Projects/VPKI/VPKI/VPKI.Library/Services/CertificateService.cs b/Projects/VPKI/VPKI/VPKI.Library/Services/CertificateService.cs index dfc7e3b..fc3931a 100644 --- a/Projects/VPKI/VPKI/VPKI.Library/Services/CertificateService.cs +++ b/Projects/VPKI/VPKI/VPKI.Library/Services/CertificateService.cs @@ -573,12 +573,7 @@ namespace VPKI.Library.Services hashedCsr.SignedCsr = GetKeyBase64Encrypted(vnSignedHash); hashedCsr.EncodedSignedCsr = GetKeyBase64Encrypted(vnDEREncVal); - hashedCsr.Verify = bVerfRst; - - Log4net.WriteLine($"SignHashed02 csr::{csr}", LogType.Fatal); - Log4net.WriteLine($"SignHashed02 Public Key::{GetKeyBase64Encrypted(GetPublicKeyBC(keyPair)!.Q.GetEncoded()!.ToList())}", LogType.Fatal); - Log4net.WriteLine($"SignHashed02 SignedCsr::{hashedCsr.SignedCsr}", LogType.Fatal); - Log4net.WriteLine($"SignHashed02 EncodedSignedCsr::{hashedCsr.EncodedSignedCsr}", LogType.Fatal); + hashedCsr.Verify = bVerfRst; var decoded = DecodeDERSignature_02(vnDEREncVal); @@ -601,11 +596,7 @@ namespace VPKI.Library.Services List vnSignedHash = GetBouncyCastleECDSASignedHashKey(vnRHashVal, GetPrivateKeyBC(keyPair), ECDSAType.NONEWITHECDSA); List vnDEREncVal = EncodeDERSignature_20(vnSignedHash); bool bVerfRst = VerifyHashBC(vnSignedHash, vnRHashVal, GetPublicKeyBC(keyPair), ECDSAType.NONEWITHECDSA); - - Log4net.WriteLine($"SignHashed20 csr::{csr}", LogType.Fatal); - Log4net.WriteLine($"SignHashed20 Public Key::{GetKeyBase64Encrypted(GetPublicKeyBC(keyPair)!.Q.GetEncoded()!.ToList())}", LogType.Fatal); - Log4net.WriteLine($"SignHashed20 bVerfRst::{bVerfRst}", LogType.Fatal); - + hashedCsr.SignedCsr = GetKeyBase64Encrypted(vnSignedHash); hashedCsr.EncodedSignedCsr = GetKeyBase64Encrypted(vnDEREncVal); hashedCsr.Verify = bVerfRst; diff --git a/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/CommonController.cs b/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/CommonController.cs index 9cbca4c..ded161d 100644 --- a/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/CommonController.cs +++ b/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/CommonController.cs @@ -8,7 +8,7 @@ using VPKI.Library.Services; namespace VPKI.Web.Api.Controllers { - public class CommonController + public class CommonController : ControllerBase { public readonly IServiceProvider _serviceProvider; public readonly IHttpContextAccessor _httpContextAccessor; diff --git a/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/VPKIBaseController.cs b/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/VPKIBaseController.cs index 28c3638..4b36407 100644 --- a/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/VPKIBaseController.cs +++ b/Projects/VPKI/VPKI/VPKI.Web.Api/Controllers/VPKIBaseController.cs @@ -1,7 +1,12 @@ -using Microsoft.AspNetCore.Http; +using DB.VPKI_DataDB; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; +using System.IO; +using System.Text; using VPKI.Library.Enums; using VPKI.Library.Model; +using VPKI.Library.Static; using VPKI.Web.Api.Services; namespace VPKI.Web.Api.Controllers @@ -19,7 +24,7 @@ namespace VPKI.Web.Api.Controllers [Tags("VPKI API Base")] [HttpGet] [Route("history")] - public async Task TbscsrHistory(DateTime startDate, DateTime endDate) + public async Task TbscsrHistory(DateTime startDate, DateTime endDate, [FromQuery] TTbscsr filter, int page = 1) { List response = new List(); @@ -27,7 +32,7 @@ namespace VPKI.Web.Api.Controllers var service = _serviceProvider.GetService(); if (service != null) { - response = await service.History(DateOnly.FromDateTime(startDate), DateOnly.FromDateTime(endDate)); + response = await service.History(DateOnly.FromDateTime(startDate), DateOnly.FromDateTime(endDate), filter, page); } long min = 0; @@ -129,5 +134,40 @@ namespace VPKI.Web.Api.Controllers return Results.Ok(response); } + + /// + /// csv + /// + [Tags("VPKI API Base")] + [HttpGet] + [Route("exportCsv")] + [ApiExplorerSettings(IgnoreApi = false)] + public async Task ExportCsv(DateTime startDate, DateTime endDate, [FromQuery] TTbscsr filter) + { + Log4net.WriteLine(GetRequestLog().LogModelToString("Request"), LogType.CONTROLLER); + + var sb = new StringBuilder(); + int row = 0; + + var service = _serviceProvider.GetService(); + if (service != null) + { + var csvData = await service.History(DateOnly.FromDateTime(startDate), DateOnly.FromDateTime(endDate), filter, -1); + row = csvData.Count; + + foreach (var line in csvData.ConvertToCsvFile()) + { + sb.AppendLine(line); + } + } + Log4net.WriteLine($"Response::Export CSV, {row} Rows", LogType.CONTROLLER); + + // UTF-8 BOM 포함 (Excel에서 깨지지 않도록 하기 위해) + var bom = Encoding.UTF8.GetPreamble(); + var csvBytes = Encoding.UTF8.GetBytes(sb.ToString()); + var resultBytes = bom.Concat(csvBytes).ToArray(); + + return File(resultBytes, "text/csv", $"VPKI_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.csv"); + } } } diff --git a/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_02Service.cs b/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_02Service.cs index f7b0d5a..548e0f2 100644 --- a/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_02Service.cs +++ b/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_02Service.cs @@ -180,11 +180,7 @@ namespace VPKI.Web.Api.Services } else { - var publicKeyPem = $"{_certificateService.ConvertToPEM(Convert.FromBase64String(csr.CPublickey))}"; - Log4net.WriteLine($"Failed HashedTbscsr::{csr.CHashedTbscsr}", LogType.Fatal); - Log4net.WriteLine($"Failed Signature::{request.csrsignature}", LogType.Fatal); - Log4net.WriteLine($"Failed PublicKey::{csr.CPublickey}", LogType.Fatal); - Log4net.WriteLine($"Failed PublicKey::{publicKeyPem}", LogType.Fatal); + var publicKeyPem = $"{_certificateService.ConvertToPEM(Convert.FromBase64String(csr.CPublickey))}"; } } catch (Exception e) diff --git a/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_20Service.cs b/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_20Service.cs index 7167197..109300d 100644 --- a/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_20Service.cs +++ b/Projects/VPKI/VPKI/VPKI.Web.Api/Services/ISO15118_20Service.cs @@ -42,10 +42,10 @@ namespace VPKI.Web.Api.Services request.certType = VpkiType.vehicle_cert.ToString(); } - string pcid = _certificateService.CreatePCID(request, VpkiType.prov_v1); - string dn = $"cn={_certificateService.CreatePCID(request, VpkiType.prov_cert)},ou={request.certInfo.tierCode},ou={request.certInfo.unitCode},ou=ECC,o=HKMC,dc={request.certInfo.dc}"; - if (request.certType == VpkiType.vehicle_cert.ToString() || request.certType == VpkiType.evcc_cert.ToString()) - dn = $"cn={_certificateService.CreatePCID(request, VpkiType.vehicle_cert)},ou={request.certInfo.tierCode},ou={request.certInfo.unitCode},ou=ECC,o=HKMC,dc={request.certInfo.dc}"; + string pcid = _certificateService.CreatePCID(request, VpkiType.vehicle_cert); + if (request.certType == VpkiType.evcc_cert.ToString()) + pcid = _certificateService.CreatePCID(request, VpkiType.evcc_cert); + string dn = $"cn={pcid},ou={request.certInfo.tierCode},ou={request.certInfo.unitCode},ou=ECC,o=HKMC,dc={request.certInfo.dc}"; string csrOrigin = _certificateService.CreateCsr512(dn, request.publickey); string tbaCsrHashed = string.Empty; @@ -191,11 +191,7 @@ namespace VPKI.Web.Api.Services } else { - var publicKeyPem = $"{_certificateService.ConvertToPEM(Convert.FromBase64String(csr.CPublickey))}"; - Log4net.WriteLine($"Failed HashedTbscsr::{csr.CHashedTbscsr}", LogType.Fatal); - Log4net.WriteLine($"Failed Signature::{request.csrsignature}", LogType.Fatal); - Log4net.WriteLine($"Failed PublicKey::{csr.CPublickey}", LogType.Fatal); - Log4net.WriteLine($"Failed PublicKey::{publicKeyPem}", LogType.Fatal); + var publicKeyPem = $"{_certificateService.ConvertToPEM(Convert.FromBase64String(csr.CPublickey))}"; } } catch (Exception e) diff --git a/Projects/VPKI/VPKI/VPKI.Web.Api/Services/VpkiBaseService.cs b/Projects/VPKI/VPKI/VPKI.Web.Api/Services/VpkiBaseService.cs index 2bcc700..8b697ac 100644 --- a/Projects/VPKI/VPKI/VPKI.Web.Api/Services/VpkiBaseService.cs +++ b/Projects/VPKI/VPKI/VPKI.Web.Api/Services/VpkiBaseService.cs @@ -1,10 +1,12 @@ using Azure; using DB.VPKI_DataDB; using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; +using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; using Microsoft.Extensions.FileSystemGlobbing.Internal; using Microsoft.Identity.Client; +using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Utilities.Collections; using System.Collections.Generic; @@ -44,10 +46,13 @@ namespace VPKI.Web.Api.Services } #region 공통 사용 함수 - public async Task> History(DateOnly startDate, DateOnly endDate) + public async Task> History(DateOnly startDate, DateOnly endDate, TTbscsr filter, int page = 1) { List response = new List(); - + + int rowCount = 20; + int totalCount = 0; + using (var scope = _scopeFactory.CreateScope()) { var context = scope.ServiceProvider.GetRequiredService(); @@ -56,7 +61,22 @@ namespace VPKI.Web.Api.Services using (var transaction = await context.CreateTransactionAsync(System.Data.IsolationLevel.ReadUncommitted)) { var getList = await context.TTbscsrs.AsNoTracking().Where(x => startDate <= DateOnly.FromDateTime(x.CDateTime.Date) - && DateOnly.FromDateTime(x.CDateTime.Date) <= endDate).ToListAsync(); + && DateOnly.FromDateTime(x.CDateTime.Date) <= endDate + && EF.Functions.Like(x.CIftid, $"%{filter.CIftid}%") + && EF.Functions.Like(x.CMacaddr, $"%{filter.CMacaddr}%") + && EF.Functions.Like(x.CWmi, $"%{filter.CWmi}%") + && EF.Functions.Like(x.CIdType, $"%{filter.CIdType}%") + && EF.Functions.Like(x.CSupplierId, $"%{filter.CSupplierId}%") + && EF.Functions.Like(x.CDc, $"%{filter.CDc}%") + && EF.Functions.Like(x.CTierCode, $"%{filter.CTierCode}%") + && EF.Functions.Like(x.CUnitCode, $"%{filter.CUnitCode}%") + && EF.Functions.Like(x.CCertType, $"%{filter.CCertType}%") + && EF.Functions.Like(x.CPcid, $"%{filter.CPcid}%")).OrderByDescending(x=>x.CCuid).ToListAsync(); + + totalCount = getList.Count; + if(page >= 0) + getList = getList.Skip((page - 1) * rowCount).Take(rowCount).ToList(); + response.AddRange(getList.Select(x => new CertificateHistoryModel { TTbscsr = x, TCertificate = null })); foreach (var r in response) @@ -72,6 +92,8 @@ namespace VPKI.Web.Api.Services var ocspResult = await context.TOcsps.AsNoTracking().FirstOrDefaultAsync(x => x.CCuid == r!.TTbscsr!.CCuid); if (ocspResult != null) r.TOcsp = ocspResult; + + r.TotalCount = totalCount; } await context.CloseTransactionAsync(transaction); @@ -292,7 +314,7 @@ namespace VPKI.Web.Api.Services return subCaFile; } - + #endregion } } diff --git a/Projects/VPKI/VPKI/VPKI.Web.Client/Components/Pages/VPKIManager.razor b/Projects/VPKI/VPKI/VPKI.Web.Client/Components/Pages/VPKIManager.razor index bbc595d..bfff43b 100644 --- a/Projects/VPKI/VPKI/VPKI.Web.Client/Components/Pages/VPKIManager.razor +++ b/Projects/VPKI/VPKI/VPKI.Web.Client/Components/Pages/VPKIManager.razor @@ -31,38 +31,29 @@ - Search + Search - - + @* + *@
-
+ @*
-
+
*@
- - + + @* *@
- + - - - @* - - *@ - @foreach (var prop in typeof(TTbscsr).GetProperties()) { @@ -71,7 +62,29 @@ continue; string propName = $"{prop.Name}"; - + //filter 없음 + if (propName.ToLower().Contains("ccuid")) + { + + + + } + else if (propName.ToLower().Contains("chascertificate")) + { + + + + } + //filter 있음 + else + { + + + } } @@ -160,6 +173,8 @@ } + + @@ -172,16 +187,17 @@ private DateOnly SearchStartDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-7)); private DateOnly SearchEndDate = DateOnly.FromDateTime(DateTime.Now); - private string FilterIft = string.Empty; - private string FilterWMI = string.Empty; - private string FilterDC = string.Empty; - private string FilterCertType = string.Empty; + string pagingSummaryFormat = "Page{0}/{1} (Total:{2} Rows)"; - private int ViewCount = 20; + private int PageTotalRowCount = 0; + private int PageViewCount = 10; + private int PageSizeCount = 20; List history = new List(); RadzenDataGrid grid = new(); + Dictionary DicFilter = new Dictionary(); + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -191,7 +207,7 @@ await Task.CompletedTask; } - private async Task Search(int startCcuid) + private async Task Search(int page = 1) { DialogService.OpenIndicator("Search History"); @@ -200,33 +216,23 @@ //Default filter requestUrl += $"startDate={SearchStartDate}&"; requestUrl += $"endDate={SearchEndDate}&"; + // requestUrl += $"startCcuid={startCcuid}&"; //optional filter - // if (string.IsNullOrEmpty(FilterIft) == false) - // { - // requestUrl += $"ift={FilterIft}&"; - // } - // if (string.IsNullOrEmpty(FilterWMI) == false) - // { - // requestUrl += $"wmi={FilterWMI}&"; - // } - // if (string.IsNullOrEmpty(FilterDC) == false) - // { - // requestUrl += $"dc={FilterDC}&"; - // } - // if (string.IsNullOrEmpty(FilterCertType) == false) - // { - // requestUrl += $"certType={FilterCertType}&"; - // } + foreach (var filter in grid.Query.Filters) + { + requestUrl += $"{filter.Property.Split('.').Last()}={filter.FilterValue}&"; + } + requestUrl += $"page={page}"; history?.Clear(); var http = new Http(); var result = await http.GetJsonAsync>(requestUrl); - if (result != null) - { + if (result?.Count > 0) + { + PageTotalRowCount = result.First().TotalCount; history.AddRange(result); - history.Reverse(); } grid?.Reload(); @@ -345,51 +351,58 @@ DialogService.CloseIndicator(); } - async Task OnClickExportCSV(bool isFilter = false) + async Task OnClickExportCSV() { DialogService.OpenIndicator("Export to CSV"); - List exportData = new List(); + var http = new Http(); + string requestUrl = $"https://{ServerAddress}/exportCsv?"; - if (isFilter == true) - { - //필터링된 데이터만 - exportData.AddRange(grid.View.ToList().ConvertToCsvFile()); - } - else - { - //전체 데이터 - exportData.AddRange(history.ConvertToCsvFile()); - } + //Default filter + requestUrl += $"startDate={SearchStartDate}&"; + requestUrl += $"endDate={SearchEndDate}&"; - if (exportData?.Count > 0) + // requestUrl += $"startCcuid={startCcuid}&"; + + //optional filter + foreach (var filter in grid.Query.Filters) { - string fileName = $"VPKI_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.csv"; - bool exportResult = await JS.InvokeAsync("openSaveFileCSV", fileName, exportData.ToJson()); + requestUrl += $"{filter.Property.Split('.').Last()}={filter.FilterValue}&"; + } + var csv = await http.GetStreamAsync(requestUrl, timeOutSeconds: 30); + + string fileName = $"VPKI_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.csv"; + if(csv != null) + { + using var streamRef = new DotNetStreamReference(stream: csv); + + bool exportResult = await JS.InvokeAsync("openSaveFileCSV", fileName, streamRef); if (exportResult == true) { - NotiService.Notify(new NotificationMessage - { - Severity = NotificationSeverity.Success, - Summary = "Success", - Detail = "CSV Export Success" - }); + NotiService.Notify(new NotificationMessage + { + Severity = NotificationSeverity.Success, + Summary = "Success", + Detail = "CSV Export Success" + }); } else { NotiService.Notify(new NotificationMessage - { - Severity = NotificationSeverity.Error, - Summary = "Failed", - Detail = "CSV Export Failed", - }); + { + Severity = NotificationSeverity.Error, + Summary = "Failed", + Detail = "CSV Export Failed", + }); } } DialogService.CloseIndicator(); } - void OnChangeFilter() + void OnChangeFilter(DataGridColumnFilterEventArgs args) { + PageTotalRowCount = 0; + history?.Clear(); StateHasChanged(); } @@ -522,5 +535,10 @@ Detail = "Copy To Clipboard Failed.", }); } + } + + private async Task PageChanged(PagerEventArgs args) + { + await Search(args.PageIndex + 1); } } diff --git a/Projects/VPKI/VPKI/VPKI.Web.Client/wwwroot/js/scripts.js b/Projects/VPKI/VPKI/VPKI.Web.Client/wwwroot/js/scripts.js index 255f26f..7bd4467 100644 --- a/Projects/VPKI/VPKI/VPKI.Web.Client/wwwroot/js/scripts.js +++ b/Projects/VPKI/VPKI/VPKI.Web.Client/wwwroot/js/scripts.js @@ -1,24 +1,17 @@ window.openSaveFileCSV = async function (fileName, writeData) { try { - const handle = await window.showSaveFilePicker({ - suggestedName: fileName, // 파일의 기본 이름 - types: [ - { - description: "csv", - accept: { "text/plain": [".csv" ] } - } - ] - }); + const arrayBuffer = await writeData.arrayBuffer(); + const blob = new Blob([arrayBuffer]); + const url = URL.createObjectURL(blob); - // 파일을 선택한 후 처리 (파일 핸들러) - const writableStream = await handle.createWritable(); - const jsonData = JSON.parse(writeData) + const anchorElement = document.createElement("a"); + anchorElement.href = url; + anchorElement.download = fileName ?? "download.csv"; + anchorElement.click(); - for (var i = 0; i < jsonData.length; i++) { - await writableStream.write(jsonData[i] + "\r\n"); - } - await writableStream.close(); - + anchorElement.remove(); + URL.revokeObjectURL(url); + return true; // 선택된 파일 이름을 반환 } catch (error) { console.error("Error in showSaveFilePicker:", error);