take selfie
This commit is contained in:
		
							parent
							
								
									6f836c2449
								
							
						
					
					
						commit
						5096f79f99
					
				| @ -4,9 +4,11 @@ | ||||
| 		<TargetFramework>net9.0</TargetFramework> | ||||
| 		<Nullable>enable</Nullable> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| 		<UserSecretsId>4082ec67-5a76-4239-b16a-4ab16e73b32a</UserSecretsId> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="CompreFace.NET.Sdk" Version="1.0.2" /> | ||||
| 		<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.0" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,71 @@ | ||||
| @page | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| @model EnotaryoPH.Web.Pages.Principal.NotaryoSteps.TakeSelfieModel | ||||
| @{ | ||||
| } | ||||
| 
 | ||||
| @section Head { | ||||
|     <link href="\lib\fontawesome-free-6.7.1-web\css\all.min.css" rel="stylesheet" /> | ||||
|     <style> | ||||
|         #Photo { | ||||
|             height: 500px; | ||||
|         } | ||||
|     </style> | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| <section class="my-5"> | ||||
|     <div class="container"> | ||||
|         <div class="row"> | ||||
|             <div class="col"> | ||||
|                 <h1>Notaryo Steps</h1> | ||||
|                 <div class="mt-3"> | ||||
|                     @await Component.InvokeAsync("NotaryoSteps", new | ||||
|                         { | ||||
|                             NotaryoSteps = new List<NotaryoStep>() { | ||||
|                                 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 }, | ||||
|                         }}) | ||||
|                     <div class="tab-content mt-3"></div> | ||||
|                 </div> | ||||
|                 <form class="mt-3" method="post"> | ||||
| 
 | ||||
|                     @Html.ValidationSummary() | ||||
| 
 | ||||
|                     <div id="VideoContainer" class="bg-light"> | ||||
|                         <video id="Video" height="500" class="d-block mx-auto">Video stream not available.</video> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div class="d-none"> | ||||
|                         <canvas id="Canvas"></canvas> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div id="PhotoContainer" class="photo__container"> | ||||
|                         <div class="bg-light"> | ||||
|                             <img id="Photo" class="d-block mx-auto img img-fluid" alt="The screen capture will appear in this box."> | ||||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div class="d-flex mt-3"> | ||||
|                         <button type="button" class="btn btn-secondary btn-lg me-1" id="TakePictureButton"><i class="fas fa-camera me-2"></i>TAKE PICTURE</button> | ||||
|                         <button type="button" class="btn btn-secondary btn-lg" id="AgainButton"><i class="fas fa-redo-alt me-2"></i>AGAIN</button> | ||||
|                         <div class="flex-grow-1"></div> | ||||
|                         <button type="submit" class="btn btn-primary btn-lg" id="NextButton">NEXT<i class="fas fa-chevron-right ms-2"></i></button> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <input type="hidden" asp-for="SelfieBase64Image" /> | ||||
|                 </form> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| 
 | ||||
| @section Scripts { | ||||
|     <script src="~/lib/jquery/dist/jquery.min.js"></script> | ||||
|     <partial name="_ValidationScriptsPartial" /> | ||||
|     <script src="\dist\js\jfa.min.js"></script> | ||||
|     <script src="~/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.js" asp-append-version="true"></script> | ||||
| } | ||||
| @ -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<IActionResult> 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<string>("CompreFaceConfig:APIKey"); | ||||
|             var client = _compreFaceClient.GetCompreFaceService<VerificationService>(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; } | ||||
|     } | ||||
| } | ||||
| @ -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(); | ||||
| })(); | ||||
| @ -0,0 +1,28 @@ | ||||
| @page "{Transaction_UID}" | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| @model EnotaryoPH.Web.Pages.Principal.NotaryoSteps.UploadDocumentModel | ||||
| @{ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| <section class="my-5"> | ||||
|     <div class="container"> | ||||
|         <div class="row"> | ||||
|             <div class="col"> | ||||
|                 <h1>Notaryo Steps</h1> | ||||
|                 <div class="mt-3"> | ||||
|                     @await Component.InvokeAsync("NotaryoSteps", new | ||||
|                         { | ||||
|                             NotaryoSteps = new List<NotaryoStep>() { | ||||
|                                 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 }, | ||||
|                             } | ||||
|                         }) | ||||
|                     <div class="tab-content mt-3"></div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -16,7 +16,7 @@ | ||||
|                 <h1>Notaryo Steps</h1> | ||||
|                 <div class="mt-3"> | ||||
|                     @await Component.InvokeAsync("NotaryoSteps", new { NotaryoSteps = new List<NotaryoStep>() { | ||||
|                     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 }, | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| @using EnotaryoPH.Web | ||||
| @using Microsoft.AspNetCore.Http.Extensions | ||||
| @namespace EnotaryoPH.Web.Pages | ||||
| @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers | ||||
| @addTagHelper *, EnotaryoPH | ||||
| @ -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<IPasswordService, PasswordService>(); | ||||
|             builder.Services.AddTransient<ICurrentUserService, CurrentUserService>(); | ||||
|             builder.Services.AddTransient<ICompreFaceClient>(provider => | ||||
|             { | ||||
|                 var config = provider.GetRequiredService<IConfiguration>(); | ||||
|                 var host = config.GetValue<string>("CompreFaceConfig:Host"); | ||||
|                 var port = config.GetValue<string>("CompreFaceConfig:Port"); | ||||
|                 return new CompreFaceClient(host, port); | ||||
|             }); | ||||
| 
 | ||||
|             var app = builder.Build(); | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user