Stored Procedure for UpdateUserDeletionSettings and UserDeletionService (WORKING VERSION)

PHOTO EMBED

Sat Aug 03 2024 14:02:36 GMT+0000 (Coordinated Universal Time)

Saved by @iamkatmakhafola

//SQL QUERY
CREATE PROCEDURE UpdateUserDeletionSettings
    @DeletionTimeValue INT,
    @DeletionTimeUnit NVARCHAR(20)
AS
BEGIN
    SET NOCOUNT ON;
    
    -- Define the deletion threshold
    DECLARE @DeletionThreshold DATETIME;
    SET @DeletionThreshold = CASE
        WHEN @DeletionTimeUnit = 'Minutes' THEN DATEADD(MINUTE, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Hours' THEN DATEADD(HOUR, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Days' THEN DATEADD(DAY, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Weeks' THEN DATEADD(WEEK, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Months' THEN DATEADD(MONTH, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Years' THEN DATEADD(YEAR, -@DeletionTimeValue, GETUTCDATE())
        ELSE GETUTCDATE()
    END;

    -- Delete users based on the threshold
    DELETE FROM dbo.AspNetUsers
    WHERE User_Status_ID = 2 AND DeactivatedAt < @DeletionThreshold;
END

//UserDeletionService
using av_motion_api.Data; 
using av_motion_api.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace av_motion_api.Services
{
    public class UserDeletionService : IHostedService, IDisposable
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IOptionsMonitor<DeletionSettings> _settings;
        private Timer _timer;
        private long _remainingIntervals;
        private const long MaxInterval = Int32.MaxValue - 2;

        public UserDeletionService(IServiceProvider serviceProvider, IOptionsMonitor<DeletionSettings> settings)
        {
            _serviceProvider = serviceProvider;
            _settings = settings;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            ScheduleDeletionTask();
            _settings.OnChange(settings => ScheduleDeletionTask());
            return Task.CompletedTask;
        }

        private void ScheduleDeletionTask()
        {
            var interval = GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit);
            _remainingIntervals = (long)Math.Ceiling(interval.TotalMilliseconds / MaxInterval);

            _timer?.Dispose();
            _timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(MaxInterval));
        }



        private void OnTimerElapsed(object state)
        {
            if (--_remainingIntervals <= 0)
            {
                DeleteDeactivatedUsers(state);
                ScheduleDeletionTask(); // Reschedule for the next interval
            }
        }

        private void DeleteDeactivatedUsers(object state)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();

                var deletionThreshold = DateTime.UtcNow.Subtract(GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit));

                var connection = context.Database.GetDbConnection();
                if (connection.State != System.Data.ConnectionState.Open)
                {
                    connection.Open();
                }

                using (var command = connection.CreateCommand())
                {
                    command.CommandText = "UpdateUserDeletionSettings";
                    command.CommandType = System.Data.CommandType.StoredProcedure;
                    command.Parameters.Add(new SqlParameter("@DeletionTimeValue", _settings.CurrentValue.DeletionTimeValue));
                    command.Parameters.Add(new SqlParameter("@DeletionTimeUnit", _settings.CurrentValue.DeletionTimeUnit));

                    command.ExecuteNonQuery();
                }
            }
        }


        private TimeSpan GetTimeSpan(int value, string unit)
        {
            return unit.ToLower() switch
            {
                "minutes" => TimeSpan.FromMinutes(value),
                "hours" => TimeSpan.FromHours(value),
                "days" => TimeSpan.FromDays(value),
                "weeks" => TimeSpan.FromDays(value * 7),
                "months" => TimeSpan.FromDays(value * 30), // Approximation
                "years" => TimeSpan.FromDays(value * 365), // Approximation
                _ => TimeSpan.FromMinutes(value),
            };
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}

//Program.cs --unchanged
using av_motion_api.Data;
using av_motion_api.Factory;
using av_motion_api.Models;
using av_motion_api.Interfaces;
using av_motion_api.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration; // Ensure this is here
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Configure the app environment
var configuration = builder.Configuration;

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
    //.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false);
    .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); // Add reloadOnChange

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    //config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
    config.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); // Add reloadOnChange
    config.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
});

// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll", policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
}

// Add services to the container
builder.Services.AddControllers()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
                    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                });

// SQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

builder.Services.AddIdentity<User, Role>(options =>
                {
                    options.Password.RequireUppercase = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireDigit = true;
                    options.User.RequireUniqueEmail = true;
                })
                .AddRoles<Role>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration["Tokens:Issuer"],
                        ValidAudience = builder.Configuration["Tokens:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Tokens:Key"]))
                    };
                });

// Configure FormOptions for file uploads
builder.Services.Configure<FormOptions>(o =>
{
    o.ValueLengthLimit = int.MaxValue;
    o.MultipartBodyLengthLimit = int.MaxValue;
    o.MemoryBufferThreshold = int.MaxValue;
});

builder.Services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipalFactory>();

builder.Services.Configure<DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromHours(3));


// Register the OrderStatusUpdater hosted service
builder.Services.AddHostedService<OrderStatusUpdater>();

// Register the DeletionSettings configuration section
builder.Services.Configure<DeletionSettings>(configuration.GetSection("DeletionSettings"));

// Register the UserDeletionService hosted service
builder.Services.AddHostedService<UserDeletionService>();

// Register ContractLinkingService
//builder.Services.AddHostedService<ContractLinkingService>();


// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Use CORS
app.UseCors("AllowAll");

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Handling request: " + context.Request.Path);
    await next.Invoke();
    logger.LogInformation("Finished handling request.");
});

app.Run();

//appsettings.Development.json --unchanged
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=DESKTOP-9QI4G7U\\SQLEXPRESS;Database=AV_Motion;Trusted_Connection=True;TrustServerCertificate=True"
  },
  "Tokens": {
    "Key": "hLKjZkF5q2wX3rQ$@1hy+VRv[&)0XhxJ<sk=yUpW{yE5CH@xh",
    "Issuer": "https://localhost:7185",
    "Audience": "http://localhost:4200"
  },
  "SendGrid": {
    "ApiKey": "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",
    "SenderEmail": "datalungeteam43@gmail.com",
    "SenderName": "AVSFitnessAdmin@AVMotion.com",
    "ContractsDirectory": "C:\\Contracts\\AVS_Fitness"
  },
  "DeletionSettings": {
    "DeletionTimeValue": 1,
    "DeletionTimeUnit": "Minutes"
  }
}
  
//AppDbContext --Add & run migration
  public DbSet<DeletionSettings> DeletionSettings { get; set; }

  // Configure the DeletionSettings entity
  builder.Entity<DeletionSettings>()
    .HasKey(ds => new { ds.DeletionTimeValue, ds.DeletionTimeUnit });

  builder.Entity<DeletionSettings>()
    .Property(ds => ds.DeletionTimeValue)
    .IsRequired();

  builder.Entity<DeletionSettings>()
    .Property(ds => ds.DeletionTimeUnit)
    .IsRequired()
    .HasMaxLength(50);
content_copyCOPY

BACKEND FOR USERDELETION WITH