Added puppeteerConfig. Setup timing logic for keeping users logged in

This commit is contained in:
William Lewis 2024-03-29 12:39:29 -05:00
parent 0944642656
commit 06b461985c
10 changed files with 151 additions and 62 deletions

View File

@ -30,6 +30,7 @@
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.13" />
<PackageReference Include="PuppeteerSharp" Version="15.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.35.0" />
</ItemGroup>

View File

@ -1,6 +0,0 @@
namespace AAIntegration.SimmonsBank.API.Configs;
public class EnvelopeFundConfig
{
public int CheckIntervalInMinutes { get; set; }
}

View File

@ -0,0 +1,15 @@
namespace AAIntegration.SimmonsBank.API.Configs;
public class PuppeteerConfig
{
public int KeepAliveIntervalMinutes { get; set; }
public int CheckForNewDataIntervalMinutes { get; set; }
public int TaskCheckIntervalMinutes { get; set; }
}
public class PuppeteerConfigConstants
{
public const string KeepAliveIntervalMinutes = "KeepAliveIntervalMinutes";
public const string CheckForNewDataIntervalMinutes = "CheckForNewDataIntervalMinutes";
public const string TaskCheckIntervalMinutes = "TaskCheckIntervalMinutes";
}

View File

@ -10,5 +10,4 @@ public class AccountCreateRequest
public string InitialBalance { get; set; }
public int Currency { get; set; }
public string ExternalAccountNumber { get; set; }
public int Owner { get; set; }
}

View File

@ -1,9 +1,10 @@
using System.Threading.Tasks;
using AAIntegration.SimmonsBank.API.Entities;
using AAIntegration.SimmonsBank.API.Services;
namespace AAIntegration.SimmonsBank.API.Processes;
public interface IPuppeteerProcess
{
Task FundEnvelopes();
Task StayLoggedIn(User user);
}

View File

@ -6,48 +6,33 @@ using AAIntegration.SimmonsBank.API.Entities;
using AAIntegration.SimmonsBank.API.Models.Transactions;
using AAIntegration.SimmonsBank.API.Services;
using Internal;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
namespace AAIntegration.SimmonsBank.API.Processes;
public class PuppeteerProcess : IPuppeteerProcess
{
private readonly EnvelopeFundConfig _config;
private readonly PuppeteerConfig _config;
private readonly ILogger<PuppeteerProcess> _logger;
private readonly IMemoryCache _memoryCache;
public PuppeteerProcess(
IOptions<EnvelopeFundConfig> config,
ILogger<PuppeteerProcess> logger)
IOptions<PuppeteerConfig> config,
ILogger<PuppeteerProcess> logger,
IMemoryCache memoryCache)
{
_config = config.Value;
_logger = logger;
_memoryCache = memoryCache;
}
public async Task FundEnvelopes()
public async Task StayLoggedIn(User user)
{
_logger.LogInformation($"Starting envelope funding...");
// Grab envelopes
/*List<Envelope> envelopes = envelopeService.GetAll().ToList();
bool anyTriggered = false;
foreach (Envelope env in envelopes)
{
// Check if needs triggering
if (env.Enabled && !envelopeService.HasBeenFundedThisMonth(env.Id))
{
_logger.LogInformation($"Envelope '{env.Name}' (id={env.Id}) needs funding - Has now been triggered.");
anyTriggered = true;
// Fund Envelope
envelopeService.FundEnvelopeForThisMonth(env.Id);
_logger.LogInformation($"... doing work and processing for user {user.Id} ...");
}
}*/
if (false)
_logger.LogInformation($"No action taken.");
else
_logger.LogInformation($"Envelope funding complete.");
}
// Helper Functions
}

View File

@ -70,7 +70,7 @@ internal class Program
// Configure strongly typed settings object
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));
builder.Services.Configure<EnvelopeFundConfig>(builder.Configuration.GetSection("EnvelopeFund"));
builder.Services.Configure<PuppeteerConfig>(builder.Configuration.GetSection("Puppeteer"));
builder.Services.Configure<DatabaseConfig>(builder.Configuration.GetSection("ActiveAllocator:Database"));
DatabaseConfig dbConfig = builder.Configuration.GetSection("ActiveAllocator:Database").Get<DatabaseConfig>();

View File

@ -24,6 +24,7 @@ public interface IUserService
void Delete(string apiKey);
Dictionary<string, int> GetAllApiKeys();
User GetUser(string ApiKey);
IEnumerable<User> GetAll();
/* Other cringe way
AuthenticateResponse Authenticate(AuthenticateRequest model);
@ -116,6 +117,11 @@ public class UserService : IUserService
return user;
}
public IEnumerable<User> GetAll()
{
return _context.Users;
}
// helper methods
private User getUser(int id)

View File

@ -4,50 +4,136 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using AAIntegration.SimmonsBank.API.Processes;
using AAIntegration.SimmonsBank.API.Services;
using AAIntegration.SimmonsBank.API.Entities;
using Microsoft.Extensions.Caching.Memory;
namespace AAIntegration.SimmonsBank.API.Workers;
public class PuppeteerWorker : BackgroundService
{
private readonly EnvelopeFundConfig _config;
private readonly PuppeteerConfig _config;
private readonly IPuppeteerProcess _puppeteerProcess;
private readonly ILogger<PuppeteerWorker> _logger;
private readonly IMemoryCache _memoryCache;
private readonly IServiceScopeFactory _serviceScopeFactory;
public PuppeteerWorker(
IOptions<EnvelopeFundConfig> config,
IOptions<PuppeteerConfig> config,
IPuppeteerProcess puppeteerProcess,
ILogger<PuppeteerWorker> logger,
IMemoryCache memoryCache,
IServiceScopeFactory serviceScopeFactory)
{
_config = config.Value;
_puppeteerProcess = puppeteerProcess;
_logger = logger;
_memoryCache = memoryCache;
_serviceScopeFactory = serviceScopeFactory;
}
private IUserService _userService;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
DateTime? lastExecutedOn = null;
bool operationOccurred = false;
using (var scope = _serviceScopeFactory.CreateScope())
{
//IEnvelopeService envelopeService = scope.ServiceProvider.GetService<IEnvelopeService>();
_userService = scope.ServiceProvider.GetService<IUserService>();
// This is how we keep the app running (in the background)
while (!stoppingToken.IsCancellationRequested)
{
if (lastExecutedOn != null)
// Keep Alive processing
operationOccurred = await ProcessUsersKeepalive();
_logger.LogInformation($"Operation occurred? {operationOccurred}");
// If no operation occurred, waits before looping again
if (lastExecutedOn != null && operationOccurred == false)
{
int minutesRemaining = _config.CheckIntervalInMinutes - (int)DateTime.UtcNow.Subtract(lastExecutedOn.Value).TotalMinutes;
await Task.Delay(TimeSpan.FromMinutes(minutesRemaining < 0 ? 0 : minutesRemaining), stoppingToken);
await Task.Delay(TimeSpan.FromMinutes(_config.TaskCheckIntervalMinutes), stoppingToken);
}
lastExecutedOn = DateTime.UtcNow;
_logger.LogInformation("EnvelopeFundWorker running at: {time}", DateTimeOffset.Now);
//await _puppeteerProcess.FundEnvelopes(envelopeService);
_logger.LogInformation("PuppeteerWorker executed at: {time}", DateTimeOffset.Now);
}
}
}
private async Task<bool> ProcessUsersKeepalive()
{
bool operationOccurred = false;
foreach (User user in _userService.GetAll().ToList())
{
int ka = _config.KeepAliveIntervalMinutes;
int uka = (int)DateTime.UtcNow.Subtract(GetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id)).TotalMinutes;
//_logger.LogInformation($"KeepAlive configured to {ka}. This user hasn't been kept alive in {uka} minute(s).");
if (_config.KeepAliveIntervalMinutes < (int)DateTime.UtcNow.Subtract(GetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id)).TotalMinutes)
{
await _puppeteerProcess.StayLoggedIn(user);
SetUserLastExecution(PuppeteerConfigConstants.KeepAliveIntervalMinutes, user.Id, DateTime.UtcNow);
operationOccurred = true;
_logger.LogInformation($"Operation set to true because of user {user.Id}");
}
}
return operationOccurred;
}
private DateTime GetUserLastExecution(string cacheKey, int userId)
{
var internalKeys = GetLastExecutionCachedDictionary(cacheKey);
if (!internalKeys.TryGetValue(userId, out var lastExecution))
{
_logger.LogInformation($"Could not find userId in '{cacheKey}' cache. Returning '{DateTime.MinValue}'.");
return DateTime.MinValue;
}
_logger.LogInformation($"Just got user {userId} from cache with value {lastExecution}");
return lastExecution;
}
private void SetUserLastExecution(string cacheKey, int userId, DateTime lastExecution)
{
var internalKeys = GetLastExecutionCachedDictionary(cacheKey);
if (internalKeys.ContainsKey(userId))
internalKeys[userId] = lastExecution;
else
internalKeys.Add(userId, lastExecution);
_memoryCache.Set(cacheKey, internalKeys);
_logger.LogInformation($"Just set user {userId} into cache with value {lastExecution}");
}
private Dictionary<int, DateTime> GetLastExecutionCachedDictionary(string cacheKey)
{
// Sets cache if no dictionary is found
if (!_memoryCache.TryGetValue<Dictionary<int, DateTime>>(cacheKey, out var internalKeys))
{
internalKeys = _userService.GetAll()
.ToDictionary(u => u.Id, u => DateTime.MinValue);
_memoryCache.Set(cacheKey, internalKeys);
_logger.LogInformation($"Updated users in '{cacheKey}' cache with new id list.");
}
return internalKeys;
}
private void PrintCacheValues(string cacheKey)
{
_logger.LogInformation($"Printing cache values from '{cacheKey}'");
Dictionary<int, DateTime> internalKeys = GetLastExecutionCachedDictionary(cacheKey);
foreach (KeyValuePair<int, DateTime> entry in internalKeys)
{
_logger.LogInformation($" {entry.Key} <=> {entry.Value}");
}
}
}

View File

@ -25,7 +25,9 @@
"Secret": "5de80277015f9fd564c4d1cc2cf827dbb1774cd66e7d79aa258d9c35a9f67f32fc6cf0dc24244242bd9501288e0fd69e315b",
"APIUrl": "https://localhost:7260"
},
"EnvelopeFund": {
"CheckIntervalInMinutes": 10
"Puppeteer": {
"TaskCheckIntervalMinutes": 1,
"KeepAliveIntervalMinutes": 10,
"CheckForNewDataIntervalMinutes": 15
}
}