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="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.13" /> <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="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.35.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.35.0" />
</ItemGroup> </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 string InitialBalance { get; set; }
public int Currency { get; set; } public int Currency { get; set; }
public string ExternalAccountNumber { get; set; } public string ExternalAccountNumber { get; set; }
public int Owner { get; set; }
} }

View File

@ -1,9 +1,10 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using AAIntegration.SimmonsBank.API.Entities;
using AAIntegration.SimmonsBank.API.Services; using AAIntegration.SimmonsBank.API.Services;
namespace AAIntegration.SimmonsBank.API.Processes; namespace AAIntegration.SimmonsBank.API.Processes;
public interface IPuppeteerProcess 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.Models.Transactions;
using AAIntegration.SimmonsBank.API.Services; using AAIntegration.SimmonsBank.API.Services;
using Internal; using Internal;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace AAIntegration.SimmonsBank.API.Processes; namespace AAIntegration.SimmonsBank.API.Processes;
public class PuppeteerProcess : IPuppeteerProcess public class PuppeteerProcess : IPuppeteerProcess
{ {
private readonly EnvelopeFundConfig _config; private readonly PuppeteerConfig _config;
private readonly ILogger<PuppeteerProcess> _logger; private readonly ILogger<PuppeteerProcess> _logger;
private readonly IMemoryCache _memoryCache;
public PuppeteerProcess( public PuppeteerProcess(
IOptions<EnvelopeFundConfig> config, IOptions<PuppeteerConfig> config,
ILogger<PuppeteerProcess> logger) ILogger<PuppeteerProcess> logger,
IMemoryCache memoryCache)
{ {
_config = config.Value; _config = config.Value;
_logger = logger; _logger = logger;
_memoryCache = memoryCache;
} }
public async Task FundEnvelopes() public async Task StayLoggedIn(User user)
{ {
_logger.LogInformation($"Starting envelope funding..."); _logger.LogInformation($"... doing work and processing for user {user.Id} ...");
// 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);
} }
}*/
if (false) // Helper Functions
_logger.LogInformation($"No action taken.");
else
_logger.LogInformation($"Envelope funding complete.");
}
} }

View File

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

View File

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

View File

@ -4,50 +4,136 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using AAIntegration.SimmonsBank.API.Processes; using AAIntegration.SimmonsBank.API.Processes;
using AAIntegration.SimmonsBank.API.Services; using AAIntegration.SimmonsBank.API.Services;
using AAIntegration.SimmonsBank.API.Entities;
using Microsoft.Extensions.Caching.Memory;
namespace AAIntegration.SimmonsBank.API.Workers; namespace AAIntegration.SimmonsBank.API.Workers;
public class PuppeteerWorker : BackgroundService public class PuppeteerWorker : BackgroundService
{ {
private readonly EnvelopeFundConfig _config; private readonly PuppeteerConfig _config;
private readonly IPuppeteerProcess _puppeteerProcess; private readonly IPuppeteerProcess _puppeteerProcess;
private readonly ILogger<PuppeteerWorker> _logger; private readonly ILogger<PuppeteerWorker> _logger;
private readonly IMemoryCache _memoryCache;
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
public PuppeteerWorker( public PuppeteerWorker(
IOptions<EnvelopeFundConfig> config, IOptions<PuppeteerConfig> config,
IPuppeteerProcess puppeteerProcess, IPuppeteerProcess puppeteerProcess,
ILogger<PuppeteerWorker> logger, ILogger<PuppeteerWorker> logger,
IMemoryCache memoryCache,
IServiceScopeFactory serviceScopeFactory) IServiceScopeFactory serviceScopeFactory)
{ {
_config = config.Value; _config = config.Value;
_puppeteerProcess = puppeteerProcess; _puppeteerProcess = puppeteerProcess;
_logger = logger; _logger = logger;
_memoryCache = memoryCache;
_serviceScopeFactory = serviceScopeFactory; _serviceScopeFactory = serviceScopeFactory;
} }
private IUserService _userService;
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
DateTime? lastExecutedOn = null; DateTime? lastExecutedOn = null;
bool operationOccurred = false;
using (var scope = _serviceScopeFactory.CreateScope()) 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) // This is how we keep the app running (in the background)
while (!stoppingToken.IsCancellationRequested) 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(_config.TaskCheckIntervalMinutes), stoppingToken);
await Task.Delay(TimeSpan.FromMinutes(minutesRemaining < 0 ? 0 : minutesRemaining), stoppingToken);
} }
lastExecutedOn = DateTime.UtcNow; lastExecutedOn = DateTime.UtcNow;
_logger.LogInformation("EnvelopeFundWorker running at: {time}", DateTimeOffset.Now); _logger.LogInformation("PuppeteerWorker executed at: {time}", DateTimeOffset.Now);
}
}
}
//await _puppeteerProcess.FundEnvelopes(envelopeService); 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", "Secret": "5de80277015f9fd564c4d1cc2cf827dbb1774cd66e7d79aa258d9c35a9f67f32fc6cf0dc24244242bd9501288e0fd69e315b",
"APIUrl": "https://localhost:7260" "APIUrl": "https://localhost:7260"
}, },
"EnvelopeFund": { "Puppeteer": {
"CheckIntervalInMinutes": 10 "TaskCheckIntervalMinutes": 1,
"KeepAliveIntervalMinutes": 10,
"CheckForNewDataIntervalMinutes": 15
} }
} }