From 5096f79f99921f60d593b28314d3ea70ee14db9d Mon Sep 17 00:00:00 2001 From: jojo aquino Date: Fri, 27 Dec 2024 07:33:09 +0000 Subject: [PATCH] take selfie --- .../EnotaryoPH.Web/EnotaryoPH.Web.csproj | 2 + .../Principal/NotaryoSteps/TakeSelfie.cshtml | 71 ++++++++ .../NotaryoSteps/TakeSelfie.cshtml.cs | 167 ++++++++++++++++++ .../NotaryoSteps/TakeSelfie.cshtml.js | 96 ++++++++++ .../NotaryoSteps/UploadDocument.cshtml | 28 +++ .../NotaryoSteps/UploadDocument.cshtml.cs | 15 ++ .../NotaryoSteps/UploadIdentification.cshtml | 2 +- .../UploadIdentification.cshtml.js | 3 - .../EnotaryoPH.Web/Pages/_ViewImports.cshtml | 1 + EnotaryoPH/EnotaryoPH.Web/Program.cs | 9 +- EnotaryoPH/EnotaryoPH.Web/appsettings.json | 7 +- 11 files changed, 395 insertions(+), 6 deletions(-) create mode 100644 EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml create mode 100644 EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.cs create mode 100644 EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.js create mode 100644 EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml create mode 100644 EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml.cs diff --git a/EnotaryoPH/EnotaryoPH.Web/EnotaryoPH.Web.csproj b/EnotaryoPH/EnotaryoPH.Web/EnotaryoPH.Web.csproj index 1078121..ce9e5ae 100644 --- a/EnotaryoPH/EnotaryoPH.Web/EnotaryoPH.Web.csproj +++ b/EnotaryoPH/EnotaryoPH.Web/EnotaryoPH.Web.csproj @@ -4,9 +4,11 @@ net9.0 enable enable + 4082ec67-5a76-4239-b16a-4ab16e73b32a + diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml new file mode 100644 index 0000000..595cc99 --- /dev/null +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml @@ -0,0 +1,71 @@ +@page +@using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps +@model EnotaryoPH.Web.Pages.Principal.NotaryoSteps.TakeSelfieModel +@{ +} + +@section Head { + + +} + + +
+
+
+
+

Notaryo Steps

+
+ @await Component.InvokeAsync("NotaryoSteps", new + { + NotaryoSteps = new List() { + new NotaryoStep { Name = "Upload Identification", Step = 1 }, + new NotaryoStep { Name = "Take Selfie", Step = 2, IsActive = true }, + new NotaryoStep { Name = "Upload Document", Step = 3 }, + new NotaryoStep { Name = "Choose Notary", Step = 4 }, + }}) +
+
+
+ + @Html.ValidationSummary() + +
+ +
+ +
+ +
+ +
+
+ The screen capture will appear in this box. +
+
+ +
+ + +
+ +
+ + +
+
+
+
+
+ + +@section Scripts { + + + + +} \ No newline at end of file diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.cs b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.cs new file mode 100644 index 0000000..45b824c --- /dev/null +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.cs @@ -0,0 +1,167 @@ +using System.ComponentModel.DataAnnotations; +using EnotaryoPH.Data; +using EnotaryoPH.Data.Constants; +using EnotaryoPH.Data.Entities; +using Exadel.Compreface.Clients.CompreFaceClient; +using Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; +using Exadel.Compreface.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps +{ + public class TakeSelfieModel : PageModel + { + private readonly NotaryoDBContext _notaryoDBContext; + private readonly ICurrentUserService _currentUserService; + private readonly ICompreFaceClient _compreFaceClient; + private readonly IConfiguration _configuration; + + public TakeSelfieModel(NotaryoDBContext notaryoDBContext, ICurrentUserService currentUserService, ICompreFaceClient compreFaceClient, IConfiguration configuration) + { + _notaryoDBContext = notaryoDBContext; + _currentUserService = currentUserService; + _compreFaceClient = compreFaceClient; + _configuration = configuration; + } + + public void OnGet() + { + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = _notaryoDBContext.Users.FirstOrDefault(u => u.User_UID == _currentUserService.GetUser_UID()); + + TransactionSelfie selfieEntity; + if (TransactionSelfie_UID == Guid.Empty) + { + selfieEntity = new TransactionSelfie + { + CreatedOn = DateTime.UtcNow, + TransactionSelfie_UID = Guid.NewGuid(), + UserID = user.UserID + }; + TransactionSelfie_UID = selfieEntity.TransactionSelfie_UID.Value; + } + else + { + selfieEntity = _notaryoDBContext.TransactionSelfies.FirstOrDefault(e => e.TransactionSelfie_UID == TransactionSelfie_UID); + } + selfieEntity.File = Convert.FromBase64String(SelfieBase64Image.Split(",")[1]); + + if (selfieEntity.TransactionID > 0) + { + _notaryoDBContext.TransactionSelfies.Update(selfieEntity); + } + else + { + _notaryoDBContext.TransactionSelfies.Add(selfieEntity); + } + _notaryoDBContext.SaveChanges(); + + var identificationDocuments = _notaryoDBContext.IdentificationDocuments.Where(e => e.UserID == user.UserID); + var identificationDocument = identificationDocuments.First(); + + var selfiePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Selfies"); + if (!System.IO.Directory.Exists(selfiePath)) + { + System.IO.Directory.CreateDirectory(selfiePath); + } + var identificationDocumentPath = System.IO.Path.Combine(selfiePath, $"{identificationDocument.IdentificationDocument_UID}.jpg"); + var selfieImagePath = System.IO.Path.Combine(selfiePath, $"{TransactionSelfie_UID}.png"); + + var apiKey = _configuration.GetValue("CompreFaceConfig:APIKey"); + var client = _compreFaceClient.GetCompreFaceService(apiKey); + var faceVerificationRequest = new FaceVerificationRequestByBytes() + { + SourceImageInBytes = selfieEntity.File, + TargetImageInBytes = identificationDocument.File, + DetProbThreshold = 0.81m, + Limit = 1, + Status = false, + FacePlugins = [] + }; + try + { + var result = await client.VerifyAsync(faceVerificationRequest); + var faceMatches = result.Result.SelectMany(x => x.FaceMatches); + + if (faceMatches.Any()) + { + var newTransaction = new Transaction + { + TransactionDate = DateTime.UtcNow, + CreatedOn = DateTime.UtcNow, + PrincipalID = user.UserID, + Status = nameof(TransactionStatus.New), + Transaction_UID = Guid.NewGuid() + }; + _notaryoDBContext.Transactions.Add(newTransaction); + selfieEntity.Transaction = newTransaction; + _notaryoDBContext.SaveChanges(); + + return RedirectToPage($"/Principal/NotaryoSteps/UploadDocument/{newTransaction.Transaction_UID}"); + } + ModelState.AddModelError("", "Face Verification Failed"); + } + catch (Exception ex) + { + // capture error here + ModelState.AddModelError("", ex.Message); + } + return Page(); + } + + //private string GetSelfieUrl() + //{ + // var qb = new QueryBuilder + // { + // { "handler", "ViewImage" }, + // { "TransactionSelfie_UID", TransactionSelfie_UID.ToString() } + // }; + // var url = $"{Request.Scheme}://{Request.Host}/Principal/NotaryoSteps/TakeSelfie" + qb; + // return url; + //} + + //private string GetIdentificationDocumentUrl(Guid document_UID) + //{ + // var qb = new QueryBuilder + // { + // { "handler", "ViewImage" }, + // { "IdentificationDocument_UID", document_UID.ToString() } + // }; + // var url = $"{Request.Scheme}://{Request.Host}/Principal/IdentificationDocument/IdentificationDocument" + qb; + // return url; + //} + + [AllowAnonymous] + public IActionResult OnGetViewImage() + { + if (TransactionSelfie_UID == Guid.Empty) + { + return NotFound(); + } + + var selfie = _notaryoDBContext.TransactionSelfies.FirstOrDefault(e => e.TransactionSelfie_UID == TransactionSelfie_UID); + if (selfie == null) + { + return NotFound(); + } + + return File(selfie.File, "image/png"); + } + + [BindProperty, Required] + public string SelfieBase64Image { get; set; } + + [BindProperty(SupportsGet = true)] + public Guid TransactionSelfie_UID { get; set; } + } +} \ No newline at end of file diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.js b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.js new file mode 100644 index 0000000..ba98b28 --- /dev/null +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.js @@ -0,0 +1,96 @@ +"use strict"; +(function () { + const + control_againButton = document.getElementById("AgainButton"), + control_selfieBase64Image = document.getElementById("SelfieBase64Image"), + control_canvas = document.getElementById("Canvas"), + control_nextButton = document.getElementById("NextButton"), + control_photo = document.getElementById("Photo"), + control_photoContainer = document.getElementById("PhotoContainer"), + control_takePictureButton = document.getElementById("TakePictureButton"), + control_video = document.getElementById("Video"), + control_videoContainer = document.getElementById("VideoContainer"); + + function _bindEvents() { + control_video.addEventListener('canplay', _canPlay); + control_takePictureButton.addEventListener("click", _takePicture); + control_againButton.addEventListener("click", _showCamera); + } + + function _canPlay() { + let width = 400; + let height = control_video.videoHeight / (control_video.videoWidth / width); + + // Firefox currently has a bug where the height can't be read from + // the video, so we will make assumptions if this happens. + + if (isNaN(height)) { + height = width / (4 / 3); + } + control_canvas.setAttribute('width', width); + control_canvas.setAttribute('height', height); + } + + function _init() { + _loadCamera(); + _bindEvents(); + _showCamera(); + } + + function _loadCamera() { + navigator.mediaDevices.getUserMedia({ video: true, audio: false, }) + .then(function (stream) { + let options = { mimeType: self.mimeType }; + if (navigator.userAgent.indexOf('Chrome') >= 0) { + options = { mimeType: "video/webm; codecs=vp9" }; + } + + control_video.srcObject = stream; + control_video.play(); + }) + .catch(function (err) { + console.error("Unable to initialize camera: " + err); + }); + } + + function _showCamera() { + jfa.utilities.element.hide(control_photoContainer); + jfa.utilities.element.show(control_videoContainer); + + jfa.utilities.element.show(control_takePictureButton); + jfa.utilities.element.hide(control_againButton); + + jfa.utilities.element.disable(control_nextButton); + } + + function _showPhoto() { + jfa.utilities.element.show(control_photoContainer); + jfa.utilities.element.hide(control_videoContainer); + + jfa.utilities.element.show(control_againButton); + jfa.utilities.element.hide(control_takePictureButton); + + jfa.utilities.element.enable(control_nextButton); + } + + function _takePicture() { + let ratio = control_video.videoWidth / control_video.videoHeight; + let newHeight = control_canvas.height; + let newWidth = control_canvas.height * ratio; + + control_canvas.width = newWidth; + control_canvas.height = newHeight; + + const context = control_canvas.getContext('2d'); + context.drawImage(control_video, 0, 0, newWidth, newHeight); + + const data = control_canvas.toDataURL('image/jpg'); + control_photo.setAttribute('src', data); + + control_selfieBase64Image.value = data; + + _showPhoto(); + } + + _init(); +})(); \ No newline at end of file diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml new file mode 100644 index 0000000..be607c9 --- /dev/null +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml @@ -0,0 +1,28 @@ +@page "{Transaction_UID}" +@using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps +@model EnotaryoPH.Web.Pages.Principal.NotaryoSteps.UploadDocumentModel +@{ +} + + +
+
+
+
+

Notaryo Steps

+
+ @await Component.InvokeAsync("NotaryoSteps", new + { + NotaryoSteps = new List() { + new NotaryoStep { Name = "Upload Identification", Step = 1 }, + new NotaryoStep { Name = "Take Selfie", Step = 2 }, + new NotaryoStep { Name = "Upload Document", Step = 3, IsActive = true }, + new NotaryoStep { Name = "Choose Notary", Step = 4 }, + } + }) +
+
+
+
+
+
\ No newline at end of file diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml.cs b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml.cs new file mode 100644 index 0000000..caa1b00 --- /dev/null +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadDocument.cshtml.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps +{ + public class UploadDocumentModel : PageModel + { + public void OnGet() + { + } + + [BindProperty(SupportsGet = true)] + public Guid Transaction_UID { get; set; } + } +} diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml index 7ba10a5..ecd8d16 100644 --- a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml @@ -16,7 +16,7 @@

Notaryo Steps

@await Component.InvokeAsync("NotaryoSteps", new { NotaryoSteps = new List() { - new NotaryoStep { Name = "Choose Identification", Step = 1, IsActive = true }, + new NotaryoStep { Name = "Upload Identification", Step = 1, IsActive = true }, new NotaryoStep { Name = "Take Selfie", Step = 2 }, new NotaryoStep { Name = "Upload Document", Step = 3 }, new NotaryoStep { Name = "Choose Notary", Step = 4 }, diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml.js b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml.js index 17a77a0..9f3ae8a 100644 --- a/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml.js +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/Principal/NotaryoSteps/UploadIdentification.cshtml.js @@ -2,10 +2,7 @@ (function () { const control_existingDocumentsContainer = document.getElementById("ExistingDocumentsContainer"), - control_existingIdentificationDocumentCount = document.getElementById("ExistingIdentificationDocumentCount"), control_newIdentificationDocumentContainer = document.getElementById("NewIdentificationDocumentContainer"), - control_nextButton = document.querySelector(".wizard__nextbutton"), - control_uploadIdentificationForm = document.getElementById("UploadIdentificationForm"), control_uploadNewIdentificationRadios = document.getElementsByName("UploadNewIdentification"); function _bindEvents() { diff --git a/EnotaryoPH/EnotaryoPH.Web/Pages/_ViewImports.cshtml b/EnotaryoPH/EnotaryoPH.Web/Pages/_ViewImports.cshtml index fd1bac9..1931202 100644 --- a/EnotaryoPH/EnotaryoPH.Web/Pages/_ViewImports.cshtml +++ b/EnotaryoPH/EnotaryoPH.Web/Pages/_ViewImports.cshtml @@ -1,4 +1,5 @@ @using EnotaryoPH.Web +@using Microsoft.AspNetCore.Http.Extensions @namespace EnotaryoPH.Web.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, EnotaryoPH \ No newline at end of file diff --git a/EnotaryoPH/EnotaryoPH.Web/Program.cs b/EnotaryoPH/EnotaryoPH.Web/Program.cs index 7bb5521..228e195 100644 --- a/EnotaryoPH/EnotaryoPH.Web/Program.cs +++ b/EnotaryoPH/EnotaryoPH.Web/Program.cs @@ -1,6 +1,6 @@ using System.Security.Principal; using EnotaryoPH.Data; -using EnotaryoPH.Web.Common.Services; +using Exadel.Compreface.Clients.CompreFaceClient; using Microsoft.AspNetCore.Authentication.Cookies; namespace EnotaryoPH.Web @@ -28,6 +28,13 @@ namespace EnotaryoPH.Web builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(provider => + { + var config = provider.GetRequiredService(); + var host = config.GetValue("CompreFaceConfig:Host"); + var port = config.GetValue("CompreFaceConfig:Port"); + return new CompreFaceClient(host, port); + }); var app = builder.Build(); diff --git a/EnotaryoPH/EnotaryoPH.Web/appsettings.json b/EnotaryoPH/EnotaryoPH.Web/appsettings.json index a273c80..68e0513 100644 --- a/EnotaryoPH/EnotaryoPH.Web/appsettings.json +++ b/EnotaryoPH/EnotaryoPH.Web/appsettings.json @@ -7,6 +7,11 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "NotaryoDatabase": "Host=localhost; Database=enotaryodb; Username=enotaryodbuser; Password=2pW@jfXWJ" + "NotaryoDatabase": "Host=localhost; Database=enotaryodb; Username=enotaryodbuser; Password=secret" + }, + "CompreFaceConfig": { + "APIKey": "secret", + "Host": "http://localhost", + "Port": "8000" } } \ No newline at end of file