Compare commits
	
		
			4 Commits
		
	
	
		
			c189cc6955
			...
			565252382c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 565252382c | |||
| 80dc2e412c | |||
| b1250616a7 | |||
| c51a9cecc9 | 
							
								
								
									
										26
									
								
								Endpoint/Common/Attributes/CacheMaxAgeAttribute.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Endpoint/Common/Attributes/CacheMaxAgeAttribute.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Mirea.Api.Endpoint.Common.Attributes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
 | 
				
			||||||
 | 
					public class CacheMaxAgeAttribute : Attribute
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public int MaxAge { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CacheMaxAgeAttribute(int days = 0, int hours = 0, int minutes = 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MaxAge = (int)new TimeSpan(days, hours, minutes, 0).TotalSeconds;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CacheMaxAgeAttribute(int minutes) : this(0, 0, minutes)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CacheMaxAgeAttribute(bool usingSetting = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (usingSetting)
 | 
				
			||||||
 | 
					            MaxAge = -1;
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            MaxAge = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,7 @@ namespace Mirea.Api.Endpoint.Common.Services;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public static class UrlHelper
 | 
					public static class UrlHelper
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public static string CurrentDomain(HttpContext context) =>
 | 
					    public static string GetCurrentDomain(this HttpContext context) =>
 | 
				
			||||||
        context.Request.Headers["X-Forwarded-Host"].FirstOrDefault() ?? context.Request.Host.Host;
 | 
					        context.Request.Headers["X-Forwarded-Host"].FirstOrDefault() ?? context.Request.Host.Host;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static string CreateSubPath(string? path)
 | 
					    private static string CreateSubPath(string? path)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,9 +74,11 @@ public partial class SetupController(
 | 
				
			|||||||
        Response.Cookies.Append("AuthToken", token, new CookieOptions
 | 
					        Response.Cookies.Append("AuthToken", token, new CookieOptions
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Path = UrlHelper.GetSubPathWithoutFirstApiName + "api",
 | 
					            Path = UrlHelper.GetSubPathWithoutFirstApiName + "api",
 | 
				
			||||||
            Domain = UrlHelper.CurrentDomain(ControllerContext.HttpContext),
 | 
					            Domain = HttpContext.GetCurrentDomain(),
 | 
				
			||||||
 | 
					#if !DEBUG
 | 
				
			||||||
            Secure = true,
 | 
					            Secure = true,
 | 
				
			||||||
            HttpOnly = true
 | 
					            HttpOnly = true
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        return Ok(true);
 | 
					        return Ok(true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,9 +30,11 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            Expires = expires,
 | 
					            Expires = expires,
 | 
				
			||||||
            Path = UrlHelper.GetSubPathWithoutFirstApiName + "api",
 | 
					            Path = UrlHelper.GetSubPathWithoutFirstApiName + "api",
 | 
				
			||||||
            Domain = UrlHelper.CurrentDomain(ControllerContext.HttpContext),
 | 
					            Domain = HttpContext.GetCurrentDomain(),
 | 
				
			||||||
 | 
					#if !DEBUG
 | 
				
			||||||
            Secure = true,
 | 
					            Secure = true,
 | 
				
			||||||
            HttpOnly = true
 | 
					            HttpOnly = true
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Response.Cookies.Append(name, value, cookieOptions);
 | 
					        Response.Cookies.Append(name, value, cookieOptions);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,11 +17,14 @@ using System.Threading.Tasks;
 | 
				
			|||||||
namespace Mirea.Api.Endpoint.Controllers.V1;
 | 
					namespace Mirea.Api.Endpoint.Controllers.V1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[ApiVersion("1.0")]
 | 
					[ApiVersion("1.0")]
 | 
				
			||||||
 | 
					[CacheMaxAge(true)]
 | 
				
			||||||
public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConfig> config) : BaseController
 | 
					public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConfig> config) : BaseController
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    [CacheMaxAge(1, 0)]
 | 
				
			||||||
    [HttpGet("StartTerm")]
 | 
					    [HttpGet("StartTerm")]
 | 
				
			||||||
    public ActionResult<DateOnly> GetStartTerm() => config.Value.ScheduleSettings!.StartTerm;
 | 
					    public ActionResult<DateOnly> GetStartTerm() => config.Value.ScheduleSettings!.StartTerm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [CacheMaxAge(1, 0)]
 | 
				
			||||||
    [HttpGet("PairPeriod")]
 | 
					    [HttpGet("PairPeriod")]
 | 
				
			||||||
    public ActionResult<Dictionary<int, PairPeriodTime>> GetPairPeriod() => config.Value.ScheduleSettings!.PairPeriod.ConvertToDto();
 | 
					    public ActionResult<Dictionary<int, PairPeriodTime>> GetPairPeriod() => config.Value.ScheduleSettings!.PairPeriod.ConvertToDto();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -196,4 +199,4 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
 | 
				
			|||||||
            Professors = professors,
 | 
					            Professors = professors,
 | 
				
			||||||
            LectureHalls = lectureHalls
 | 
					            LectureHalls = lectureHalls
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					}
 | 
				
			||||||
							
								
								
									
										81
									
								
								Endpoint/Middleware/CacheMaxAgeMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								Endpoint/Middleware/CacheMaxAgeMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					using Cronos;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Options;
 | 
				
			||||||
 | 
					using Mirea.Api.Endpoint.Common.Attributes;
 | 
				
			||||||
 | 
					using Mirea.Api.Endpoint.Common.Settings;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Mirea.Api.Endpoint.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CacheMaxAgeMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public async Task InvokeAsync(HttpContext context)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!context.Response.StatusCode.ToString().StartsWith('2'))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await next(context);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var endpoint = context.GetEndpoint();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var actionDescriptor = endpoint?.Metadata.GetMetadata<Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (actionDescriptor == null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await next(context);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var controllerType = actionDescriptor.ControllerTypeInfo;
 | 
				
			||||||
 | 
					        var methodInfo = actionDescriptor.MethodInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var maxAgeAttribute = methodInfo.GetCustomAttribute<CacheMaxAgeAttribute>() ?? controllerType.GetCustomAttribute<CacheMaxAgeAttribute>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (maxAgeAttribute == null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await next(context);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (maxAgeAttribute.MaxAge)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case < 0:
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    DateTime? nextDate;
 | 
				
			||||||
 | 
					                    var now = DateTime.UtcNow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    using (var scope = serviceProvider.CreateScope())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var updateCronString = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<GeneralConfig>>().Value.ScheduleSettings?.CronUpdateSchedule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (string.IsNullOrEmpty(updateCronString) ||
 | 
				
			||||||
 | 
					                            !CronExpression.TryParse(updateCronString, CronFormat.Standard, out var updateCron))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            await next(context);
 | 
				
			||||||
 | 
					                            return;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        nextDate = updateCron.GetNextOccurrence(now);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (!nextDate.HasValue)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await next(context);
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    context.Response.Headers.CacheControl = "max-age=" + (int)(nextDate.Value - now).TotalSeconds;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            case > 0:
 | 
				
			||||||
 | 
					                context.Response.Headers.CacheControl = "max-age=" + maxAgeAttribute.MaxAge;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await next(context);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,7 +11,7 @@ namespace Mirea.Api.Endpoint.Middleware;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class CustomExceptionHandlerMiddleware(RequestDelegate next)
 | 
					public class CustomExceptionHandlerMiddleware(RequestDelegate next)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public async Task Invoke(HttpContext context)
 | 
					    public async Task InvokeAsync(HttpContext context)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ namespace Mirea.Api.Endpoint.Middleware;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class JwtRevocationMiddleware(RequestDelegate next)
 | 
					public class JwtRevocationMiddleware(RequestDelegate next)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public async Task Invoke(HttpContext context, IRevokedToken revokedTokenStore)
 | 
					    public async Task InvokeAsync(HttpContext context, IRevokedToken revokedTokenStore)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (context.Request.Headers.ContainsKey("Authorization"))
 | 
					        if (context.Request.Headers.ContainsKey("Authorization"))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ public class MaintenanceModeMiddleware(RequestDelegate next, IMaintenanceModeSer
 | 
				
			|||||||
        return endpoint?.Metadata.GetMetadata<MaintenanceModeIgnoreAttribute>() != null;
 | 
					        return endpoint?.Metadata.GetMetadata<MaintenanceModeIgnoreAttribute>() != null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async Task Invoke(HttpContext context)
 | 
					    public async Task InvokeAsync(HttpContext context)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (!maintenanceModeService.IsMaintenanceMode && !maintenanceModeNotConfigureService.IsMaintenanceMode || IsIgnoreMaintenanceMode(context))
 | 
					        if (!maintenanceModeService.IsMaintenanceMode && !maintenanceModeNotConfigureService.IsMaintenanceMode || IsIgnoreMaintenanceMode(context))
 | 
				
			||||||
            await next(context);
 | 
					            await next(context);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using Microsoft.AspNetCore.Builder;
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.DataProtection;
 | 
				
			||||||
using Microsoft.AspNetCore.Hosting;
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
using Microsoft.AspNetCore.HttpOverrides;
 | 
					using Microsoft.AspNetCore.HttpOverrides;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
@@ -94,13 +95,15 @@ public class Program
 | 
				
			|||||||
        builder.Services.AddJwtToken(builder.Configuration);
 | 
					        builder.Services.AddJwtToken(builder.Configuration);
 | 
				
			||||||
        builder.Services.AddSecurity(builder.Configuration);
 | 
					        builder.Services.AddSecurity(builder.Configuration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        builder.Services.AddDataProtection()
 | 
				
			||||||
 | 
					            .PersistKeysToFileSystem(new DirectoryInfo(PathBuilder.Combine("DataProtection")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var app = builder.Build();
 | 
					        var app = builder.Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        app.UseForwardedHeaders();
 | 
					        app.UseForwardedHeaders();
 | 
				
			||||||
        app.UseStaticFiles(UrlHelper.GetSubPath.TrimEnd('/'));
 | 
					        app.UseStaticFiles(UrlHelper.GetSubPath.TrimEnd('/'));
 | 
				
			||||||
        app.UseCors("AllowAll");
 | 
					        app.UseCors("AllowAll");
 | 
				
			||||||
        app.UseCustomSerilog();
 | 
					        app.UseCustomSerilog();
 | 
				
			||||||
        app.UseForwardedHeaders();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        using (var scope = app.Services.CreateScope())
 | 
					        using (var scope = app.Services.CreateScope())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -122,9 +125,10 @@ public class Program
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        app.UseCustomSwagger(app.Services);
 | 
					        app.UseCustomSwagger(app.Services);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        app.UseMiddleware<MaintenanceModeMiddleware>();
 | 
					 | 
				
			||||||
        app.UseMiddleware<CustomExceptionHandlerMiddleware>();
 | 
					        app.UseMiddleware<CustomExceptionHandlerMiddleware>();
 | 
				
			||||||
 | 
					        app.UseMiddleware<MaintenanceModeMiddleware>();
 | 
				
			||||||
        app.UseMiddleware<JwtRevocationMiddleware>();
 | 
					        app.UseMiddleware<JwtRevocationMiddleware>();
 | 
				
			||||||
 | 
					        app.UseMiddleware<CacheMaxAgeMiddleware>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        app.UseHttpsRedirection();
 | 
					        app.UseHttpsRedirection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user