aboutsummaryrefslogtreecommitdiffstats
path: root/src/Services
diff options
context:
space:
mode:
Diffstat (limited to 'src/Services')
-rw-r--r--src/Services/AssetsService.cs58
-rw-r--r--src/Services/CookieService.cs55
-rw-r--r--src/Services/EmailService.cs45
-rw-r--r--src/Services/OrderService.cs126
4 files changed, 284 insertions, 0 deletions
diff --git a/src/Services/AssetsService.cs b/src/Services/AssetsService.cs
new file mode 100644
index 0000000..bf0029a
--- /dev/null
+++ b/src/Services/AssetsService.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using IOL.Helpers;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using VSH.Data;
+using VSH.Data.Static;
+
+namespace VSH.Services;
+
+public class AssetsService
+{
+ private readonly MainDbContext _context;
+ private readonly ILogger<AssetsService> _logger;
+
+
+ public AssetsService(MainDbContext context, ILogger<AssetsService> logger) {
+ _context = context;
+ _logger = logger;
+ }
+
+ public Task RemoveUnusedProductImages() {
+ try {
+ _logger.LogDebug("Starting RemoveUnusedProductImages");
+ var inUseFileNames = new List<string>();
+
+ foreach (var productImageList in _context.Products.Select(c => c.Images).AsNoTracking()) {
+ var defaultFiles = productImageList.Select(image => image.FileName).ToList();
+ var smallFiles = defaultFiles.Select(c => c.ExtractFileName() + "-300" + c.ExtractExtension());
+ var miniFiles = defaultFiles.Select(c => c.ExtractFileName() + "-150" + c.ExtractExtension());
+ inUseFileNames.AddRange(defaultFiles);
+ inUseFileNames.AddRange(miniFiles);
+ inUseFileNames.AddRange(smallFiles);
+ }
+
+ var removedFileCount = 0;
+ if (inUseFileNames.Any()) {
+ foreach (var diskFile in Directory.EnumerateFiles(AppPaths.ProductImages.HostPath)) {
+ if (inUseFileNames.Any(c => c == diskFile))
+ continue;
+ if (File.Exists(diskFile))
+ File.Delete(diskFile);
+ removedFileCount++;
+ _logger.LogDebug("Deleted " + diskFile);
+ }
+ }
+
+ _logger.LogInformation("Removed " + removedFileCount + " unused product images");
+ return Task.CompletedTask;
+ } catch (Exception ex) {
+ _logger.LogError("Exception removing unused assets: {exception}", ex.Message);
+ return Task.CompletedTask;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Services/CookieService.cs b/src/Services/CookieService.cs
new file mode 100644
index 0000000..d73f0b6
--- /dev/null
+++ b/src/Services/CookieService.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Globalization;
+using VSH.Data.Enums;
+using VSH.Data.Miscellaneous;
+using VSH.Data.Static;
+
+namespace VSH.Services;
+
+public class CookieService
+{
+ private static Dictionary<string, string> XsrfCookieDescriptions => new() {
+ {
+ "en",
+ "This cookie is only required for submitting forms like the order submit form. The cookie is used to prevent abuse, nothing personal is saved or shared with this cookie."
+ }, {
+ "nb",
+ "Denne informasjonskapselen er bare påkrevd for å sende skjema som for eksempel bestillingsskjema. Informasjonskapselen blir brukt for å forhindre misbruk, ingen persondata blir lagret eller delt med denne informasjonskapselen."
+ },
+ };
+
+ private static Dictionary<string, string> SessionCookieDescriptions => new() {
+ {
+ "en",
+ "This cookie is only required for keeping you logged in while using this site. The cookie is purely functional and nothing personal is saved or shared with this cookie."
+ }, {
+ "nb",
+ "Denne informasjonskapselen er bare påkrevd for å holde deg logget på imens du bruker denne siden. Informasjonskapselen er bare funksjonell og ingen persondata blir lagret eller delt med denne informasjonskapselen."
+ },
+ };
+
+ private static Dictionary<string, string> CultureCookieDescriptions => new() {
+ {
+ "en", "This cookie is used to keep track of your preffered language, nothing personal is saved or shared with this cookie."
+ }, {
+ "nb",
+ "Denne informasjonskapselen bruker vi for å lagre hvilket språk du vil se siden på, ingen persondata blir lagret eller delt med denne informasjonskapselen."
+ },
+ };
+
+ public static AppCookie GetCookie(CookieType type) {
+ var isoCode = CultureInfo.DefaultThreadCurrentCulture?.TwoLetterISOLanguageName ?? Startup.DefaultCulture.TwoLetterISOLanguageName;
+ return type switch {
+ CookieType.XSRF => AppCookies.Xsrf with {
+ Description = XsrfCookieDescriptions[isoCode]
+ },
+ CookieType.SESSION => AppCookies.Session with {
+ Description = SessionCookieDescriptions[isoCode]
+ },
+ CookieType.CULTURE => AppCookies.Culture with {
+ Description = CultureCookieDescriptions[isoCode]
+ },
+ _ => default
+ };
+ }
+} \ No newline at end of file
diff --git a/src/Services/EmailService.cs b/src/Services/EmailService.cs
new file mode 100644
index 0000000..d5098e0
--- /dev/null
+++ b/src/Services/EmailService.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace VSH.Services;
+
+public class EmailService
+{
+ private readonly string _sendGridApiKey;
+ private readonly string _fromAddress;
+ private readonly string _fromName;
+ private readonly string _replyToAddress;
+ private readonly ILogger<EmailService> _logger;
+
+ public EmailService(IConfiguration configuration, ILogger<EmailService> logger) {
+ _sendGridApiKey = configuration.GetValue<string>("SENDGRID_API_KEY");
+ _fromAddress = configuration.GetValue<string>("MAIL_FROM_ADDRESS");
+ _replyToAddress = configuration.GetValue<string>("MAIL_REPLY_TO_ADDRESS");
+ _fromName = configuration.GetValue<string>("MAIL_FROM_NAME");
+ _logger = logger;
+ }
+
+ public async Task<bool> SendEmailAsync(
+ string subject,
+ string message,
+ IEnumerable<string> recipients
+ ) {
+ foreach (var recipient in recipients) {
+ if (!await SendEmailAsync(subject, message, recipient)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public Task<bool> SendEmailAsync(
+ string subject,
+ string message,
+ string recipient
+ ) {
+ return Task.FromResult(false);
+ }
+}
diff --git a/src/Services/OrderService.cs b/src/Services/OrderService.cs
new file mode 100644
index 0000000..21dfdee
--- /dev/null
+++ b/src/Services/OrderService.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using IOL.Helpers;
+using IOL.VippsEcommerce;
+using IOL.VippsEcommerce.Models.Api;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Localization;
+using VSH.Data;
+using VSH.Data.Database;
+using VSH.Data.Results;
+using VSH.Utilities;
+
+namespace VSH.Services;
+
+public class OrderService
+{
+ private readonly MainDbContext _context;
+ private readonly IStringLocalizer<SharedServiceResources> _localizer;
+ private readonly IVippsEcommerceService _vippsService;
+ private readonly string _vippsMsn;
+
+ public OrderService(
+ MainDbContext context,
+ IStringLocalizer<SharedServiceResources> localizer,
+ IVippsEcommerceService vippsService,
+ IConfiguration configuration
+ ) {
+ _context = context;
+ _localizer = localizer;
+ _vippsService = vippsService;
+ _vippsMsn = configuration.GetValue<string>("VIPPS_MSN");
+ }
+
+ public AppValidationResult ValidateOrderProducts(Order payload) {
+ var validationResult = new AppValidationResult();
+ foreach (var product in payload.Products) {
+ var dbProduct = _context.Products.SingleOrDefault(p => p.Id == product.Id);
+ if (dbProduct == default || !dbProduct.IsAvailable || !dbProduct.IsVisible) {
+ var error = new AppValidationResult.ValidationError(product.Id);
+ error.Errors.Add(string.Format(_localizer["Dette produktet er dessverre ikke tilgjengelig"]));
+ validationResult.Errors.Add(error);
+ } else if (dbProduct.Count != -1 && dbProduct.Count < product.NumberOfItems) {
+ var error = new AppValidationResult.ValidationError(product.Id);
+ error.Errors.Add(string.Format(_localizer
+ [
+ "Vi har dessverre ikke så mange eksemplarer av dette produktet"]));
+ validationResult.Errors.Add(error);
+ }
+ }
+
+ return validationResult;
+ }
+
+ public AppValidationResult ValidateOrder(Order payload) {
+ var validationResult = new AppValidationResult();
+ if (payload.ContactInfo != default) {
+ if (payload.ContactInfo.Name.IsNullOrWhiteSpace()) {
+ var error = new AppValidationResult.ValidationError();
+ error.Errors.Add(_localizer["Navn er påkrevd"]);
+ validationResult.Errors.Add(error);
+ }
+
+ if (payload.ContactInfo.EmailAddress.IsNullOrWhiteSpace()) {
+ var error = new AppValidationResult.ValidationError();
+ error.Errors.Add(_localizer["E-postadresse er påkrevd"]);
+ validationResult.Errors.Add(error);
+ } else if (!payload.ContactInfo.EmailAddress.IsValidEmailAddress()) {
+ var error = new AppValidationResult.ValidationError();
+ error.Errors.Add(_localizer["E-postadressen er ugyldig"]);
+ validationResult.Errors.Add(error);
+ }
+
+ if (payload.ContactInfo.PhoneNumber.IsNullOrWhiteSpace()) {
+ var error = new AppValidationResult.ValidationError();
+ error.Errors.Add(_localizer["Telefonnummer er påkrevd"]);
+ validationResult.Errors.Add(error);
+ } else if (!payload.ContactInfo.PhoneNumber.IsValidNorwegianPhoneNumber()) {
+ var error = new AppValidationResult.ValidationError();
+ error.Errors.Add(_localizer["Telefonnummeret er ugyldig"]);
+ validationResult.Errors.Add(error);
+ }
+ } else {
+ var error = new AppValidationResult.ValidationError();
+ error.Errors.Add(_localizer["Kontaktinfo er påkrevd"]);
+ validationResult.Errors.Add(error);
+ }
+
+ var productValidationResult = ValidateOrderProducts(payload);
+ validationResult.Errors.AddRange(productValidationResult.Errors);
+
+ return validationResult;
+ }
+
+ public async Task<Uri> GetVippsPaymentUrlAsync(Order order, string hostname) {
+ var totalAmountInOre = order.Products.Sum(c => c.NumberOfItems * c.PriceAtTimeOfOrder) * 100;
+
+ var initiatePaymentRequest = new VippsInitiatePaymentRequest {
+ CustomerInfo = new TCustomerInfo {
+ MobileNumber = order.ContactInfo.PhoneNumber
+ },
+ Transaction = new TTransactionInfoInitiate {
+ Amount = Convert.ToInt32(totalAmountInOre),
+ OrderId = order.OrderReference,
+ TimeStamp = DateTime.UtcNow.ToString("o"),
+ SkipLandingPage = false,
+ TransactionText = "Bestilling på nett",
+ UseExplicitCheckoutFlow = false
+ },
+ MerchantInfo = new TMerchantInfo {
+ FallBack = hostname + "/api/orders/vipps/payment-callback/" + order.OrderReference,
+ CallbackPrefix = hostname + "/api/orders/vipps/callbacks-for-payment-update",
+ IsApp = false,
+ MerchantSerialNumber = _vippsMsn
+ }
+ };
+
+ try {
+ var response = await _vippsService.InitiatePaymentAsync(initiatePaymentRequest);
+ return response.Url.IsNullOrWhiteSpace() ? default : new Uri(response.Url);
+ } catch (Exception e) {
+ Console.WriteLine(e);
+ throw;
+ }
+ }
+} \ No newline at end of file