Compare commits

...

14 Commits

Author SHA1 Message Date
2e48b0067f build: update ref
All checks were successful
.NET Test Pipeline / build-and-test (push) Successful in 4m13s
Build and Deploy Docker Container / build-and-deploy (push) Successful in 6m13s
2024-10-09 03:08:15 +03:00
71e8eca5f4 refactor: remove unused param 2024-10-09 03:02:35 +03:00
1f3aaca3cf sec: move token from responce to cookie 2024-10-09 03:00:26 +03:00
b49df925d4 fix: doesn't set expire time for fingerpring 2024-10-09 02:58:52 +03:00
c9ebddf27a fix: remove cache for method 2024-10-09 02:58:09 +03:00
f5739647b2 refactor: remove default produce 200 code 2024-10-07 02:45:38 +03:00
26dbf608b9 refactor: sync namespace 2024-10-07 02:25:36 +03:00
2b89dd07a9 feat: output a token when generating a token for the header 2024-10-07 02:16:20 +03:00
1c981fb7bf refactor: code restructuring 2024-10-07 02:13:35 +03:00
de5dc274d7 build: add cors for debugging frontend 2024-10-07 01:42:00 +03:00
c5ecf00932 revert: remove produced 2024-10-07 01:20:10 +03:00
412751e30f build: update ref 2024-09-30 01:10:37 +03:00
076d6498a1 fix: set to correct produces 2024-09-18 06:05:40 +03:00
88d78dfab3 build: update ref 2024-09-18 06:00:07 +03:00
42 changed files with 169 additions and 175 deletions

View File

@ -1,23 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Mirea.Api.Dto.Responses;
/// <summary>
/// Provides a JWT and RT token.
/// </summary>
public class TokenResponse
{
/// <summary>
/// A JWT token for accessing protected resources.
/// </summary>
[Required]
public required string AccessToken { get; set; }
/// <summary>
/// The date and time when the JWT token expires.
/// </summary>
/// <remarks>After this date, a new JWT token must be requested.</remarks>
[Required]
public required DateTime ExpiresIn { get; set; }
}

View File

@ -1,4 +1,4 @@
using Mirea.Api.Endpoint.Configuration.General.Settings;
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
using System.Collections.Generic;
using System.Linq;

View File

@ -3,12 +3,12 @@ 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 Mirea.Api.Endpoint.Configuration.Model;
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace Mirea.Api.Endpoint.Middleware;
namespace Mirea.Api.Endpoint.Configuration.Core.Middleware;
public class CacheMaxAgeMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
{

View File

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace Mirea.Api.Endpoint.Configuration.Core.Middleware;
public class CookieAuthorizationMiddleware(RequestDelegate next)
{
public const string JwtAuthorizationName = "_ajwt";
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Cookies.ContainsKey(JwtAuthorizationName))
context.Request.Headers.Authorization = "Bearer " + context.Request.Cookies[JwtAuthorizationName];
await next(context);
}
}

View File

@ -7,7 +7,7 @@ using System;
using System.Text.Json;
using System.Threading.Tasks;
namespace Mirea.Api.Endpoint.Middleware;
namespace Mirea.Api.Endpoint.Configuration.Core.Middleware;
public class CustomExceptionHandlerMiddleware(RequestDelegate next)
{

View File

@ -2,7 +2,7 @@
using Mirea.Api.Security.Common.Interfaces;
using System.Threading.Tasks;
namespace Mirea.Api.Endpoint.Middleware;
namespace Mirea.Api.Endpoint.Configuration.Core.Middleware;
public class JwtRevocationMiddleware(RequestDelegate next)
{

View File

@ -3,7 +3,7 @@ using Mirea.Api.Endpoint.Common.Attributes;
using Mirea.Api.Endpoint.Common.Interfaces;
using System.Threading.Tasks;
namespace Mirea.Api.Endpoint.Middleware;
namespace Mirea.Api.Endpoint.Configuration.Core.Middleware;
public class MaintenanceModeMiddleware(RequestDelegate next, IMaintenanceModeService maintenanceModeService, IMaintenanceModeNotConfigureService maintenanceModeNotConfigureService)
{

View File

@ -1,8 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Asp.Versioning;
using Microsoft.Extensions.DependencyInjection;
namespace Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
public static class ApiVersioningConfiguration
{
@ -14,14 +13,10 @@ public static class ApiVersioningConfiguration
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
});
services.AddVersionedApiExplorer(options =>
}).AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
services.AddEndpointsApiExplorer();
}
}

View File

@ -1,9 +1,9 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.General.Settings;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
namespace Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
public static class CacheConfiguration
{

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
public static class EnvironmentConfiguration
{

View File

@ -7,7 +7,7 @@ using Mirea.Api.Security.Common.Interfaces;
using System;
using System.Text;
namespace Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
public static class JwtConfiguration
{

View File

@ -2,14 +2,14 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.Model;
using Serilog;
using Serilog.Events;
using Serilog.Filters;
using Serilog.Formatting.Compact;
using System.IO;
namespace Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
public static class LoggerConfiguration
{

View File

@ -1,12 +1,12 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mirea.Api.Endpoint.Common.Services.Security;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.General.Settings;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
using Mirea.Api.Security;
using Mirea.Api.Security.Common.Interfaces;
namespace Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
public static class SecureConfiguration
{

View File

@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Asp.Versioning.ApiExplorer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
@ -9,7 +9,7 @@ using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.IO;
namespace Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
namespace Mirea.Api.Endpoint.Configuration.Core.Startup;
public static class SwaggerConfiguration
{

View File

@ -1,6 +0,0 @@
namespace Mirea.Api.Endpoint.Configuration.General.Interfaces;
public interface IIsConfigured
{
bool IsConfigured();
}

View File

@ -1,4 +1,4 @@
namespace Mirea.Api.Endpoint.Common.Settings;
namespace Mirea.Api.Endpoint.Configuration;
public interface ISaveSettings
{
void SaveSetting();

View File

@ -3,7 +3,7 @@ using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Mirea.Api.Endpoint.Common.Settings;
namespace Mirea.Api.Endpoint.Configuration.Model;
public class Admin : ISaveSettings
{

View File

@ -1,10 +1,10 @@
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Configuration.General.Settings;
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Mirea.Api.Endpoint.Common.Settings;
namespace Mirea.Api.Endpoint.Configuration.Model;
public class GeneralConfig : ISaveSettings
{

View File

@ -1,7 +1,7 @@
using Mirea.Api.Endpoint.Configuration.General.Attributes;
using Mirea.Api.Endpoint.Configuration.General.Interfaces;
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
namespace Mirea.Api.Endpoint.Configuration.General.Settings;
namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
[RequiredSettings]
public class CacheSettings : IIsConfigured

View File

@ -1,10 +1,10 @@
using Mirea.Api.DataAccess.Persistence.Common;
using Mirea.Api.Endpoint.Configuration.General.Attributes;
using Mirea.Api.Endpoint.Configuration.General.Interfaces;
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
using System;
using System.Text.Json.Serialization;
namespace Mirea.Api.Endpoint.Configuration.General.Settings;
namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
[RequiredSettings]
public class DbSettings : IIsConfigured

View File

@ -1,6 +1,6 @@
using Mirea.Api.Endpoint.Configuration.General.Interfaces;
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
namespace Mirea.Api.Endpoint.Configuration.General.Settings;
namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
public class EmailSettings : IIsConfigured
{

View File

@ -1,7 +1,7 @@
using Mirea.Api.Endpoint.Configuration.General.Attributes;
using Mirea.Api.Endpoint.Configuration.General.Interfaces;
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
namespace Mirea.Api.Endpoint.Configuration.General.Settings;
namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
[RequiredSettings]
public class LogSettings : IIsConfigured

View File

@ -1,10 +1,10 @@
using Mirea.Api.Endpoint.Configuration.General.Attributes;
using Mirea.Api.Endpoint.Configuration.General.Interfaces;
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Mirea.Api.Endpoint.Configuration.General.Settings;
namespace Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
[RequiredSettings]
public class ScheduleSettings : IIsConfigured

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;

View File

@ -1,6 +1,6 @@
using System;
namespace Mirea.Api.Endpoint.Configuration.General.Attributes;
namespace Mirea.Api.Endpoint.Configuration.Validation.Attributes;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class RequiredSettingsAttribute : Attribute;

View File

@ -0,0 +1,6 @@
namespace Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
public interface IIsConfigured
{
bool IsConfigured();
}

View File

@ -1,7 +1,7 @@
using Mirea.Api.Endpoint.Common.Interfaces;
using System;
namespace Mirea.Api.Endpoint.Configuration.General;
namespace Mirea.Api.Endpoint.Configuration.Validation;
public class SetupTokenService : ISetupToken
{

View File

@ -1,11 +1,11 @@
using Microsoft.Extensions.Options;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.General.Attributes;
using Mirea.Api.Endpoint.Configuration.General.Interfaces;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Endpoint.Configuration.Validation.Attributes;
using Mirea.Api.Endpoint.Configuration.Validation.Interfaces;
using System;
using System.Reflection;
namespace Mirea.Api.Endpoint.Configuration.General.Validators;
namespace Mirea.Api.Endpoint.Configuration.Validation.Validators;
public class SettingsRequiredValidator
{

View File

@ -1,4 +1,5 @@
using Cronos;
using Asp.Versioning;
using Cronos;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
@ -9,9 +10,9 @@ using Mirea.Api.Endpoint.Common.Attributes;
using Mirea.Api.Endpoint.Common.Exceptions;
using Mirea.Api.Endpoint.Common.Interfaces;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.General.Settings;
using Mirea.Api.Endpoint.Configuration.General.Validators;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
using Mirea.Api.Endpoint.Configuration.Validation.Validators;
using Mirea.Api.Security.Services;
using MySqlConnector;
using Npgsql;
@ -28,7 +29,6 @@ using System.Security.Cryptography;
namespace Mirea.Api.Endpoint.Controllers.Configuration;
[ApiVersion("1.0")]
[ApiController]
[MaintenanceModeIgnore]
[ApiExplorerSettings(IgnoreApi = true)]
public class SetupController(

View File

@ -1,15 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Mirea.Api.Dto.Common;
using Mirea.Api.Dto.Requests;
using Mirea.Api.Dto.Responses;
using Mirea.Api.Endpoint.Common.Attributes;
using Mirea.Api.Endpoint.Common.Exceptions;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.Core.Middleware;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Security.Common.Dto.Requests;
using Mirea.Api.Security.Services;
using System;
@ -45,13 +46,19 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
private void SetRefreshToken(string value, DateTimeOffset? expires = null)
{
SetCookie("refresh_token", value, expires);
SetCookie("user_key", Fingerprint, expires);
SetCookie("user_key", Fingerprint);
}
private void SetFirstToken(string value, DateTimeOffset? expires = null)
{
SetCookie("authentication_token", value, expires);
SetCookie("user_key", Fingerprint, expires);
SetCookie("user_key", Fingerprint);
}
private void SetAuthToken(string value, DateTimeOffset? expires = null)
{
SetCookie(CookieAuthorizationMiddleware.JwtAuthorizationName, value, expires);
SetCookie("user_key", Fingerprint);
}
[ApiExplorerSettings(IgnoreApi = true)]
@ -75,17 +82,17 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
/// then generating and returning an authentication token if successful.
/// </summary>
/// <param name="request">The login request containing the username/email and password.</param>
/// <returns>A TokenResponse containing the access token and its expiry if successful, otherwise an Unauthorized response.</returns>
/// <returns>User's AuthRoles.</returns>
[HttpPost("Login")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult<TokenResponse>> Login([FromBody] LoginRequest request)
[BadRequestResponse]
public async Task<ActionResult<AuthRoles>> Login([FromBody] LoginRequest request)
{
var userEntity = user.Value;
if (!userEntity.Username.Equals(request.Username, StringComparison.OrdinalIgnoreCase) &&
!userEntity.Email.Equals(request.Username, StringComparison.OrdinalIgnoreCase) ||
!passwordService.VerifyPassword(request.Password, userEntity.Salt, userEntity.PasswordHash))
return Unauthorized("Invalid username/email or password");
return BadRequest("Invalid username/email or password");
var token = await auth.GenerateAuthTokensAsync(new TokenRequest
{
@ -95,21 +102,19 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
}, "1");
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
SetAuthToken(token.AccessToken, token.AccessExpiresIn);
return Ok(new TokenResponse
{
AccessToken = token.AccessToken,
ExpiresIn = token.AccessExpiresIn
});
return Ok(AuthRoles.Admin);
}
/// <summary>
/// Refreshes the authentication token using the existing refresh token.
/// </summary>
/// <returns>A TokenResponse containing the new access token and its expiry if successful, otherwise an Unauthorized response.</returns>
/// <returns>User's AuthRoles.</returns>
[HttpGet("ReLogin")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult<TokenResponse>> ReLogin()
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<AuthRoles>> ReLogin()
{
if (string.IsNullOrEmpty(RefreshToken))
return Unauthorized();
@ -127,16 +132,13 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
);
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
SetAuthToken(token.AccessToken, token.AccessExpiresIn);
return Ok(new TokenResponse
{
AccessToken = token.AccessToken,
ExpiresIn = token.AccessExpiresIn
});
return Ok(AuthRoles.Admin);
}
catch (SecurityException)
{
return Unauthorized();
return Forbid();
}
}
@ -151,6 +153,7 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
{
SetRefreshToken("", DateTimeOffset.MinValue);
SetFirstToken("", DateTimeOffset.MinValue);
SetAuthToken("", DateTimeOffset.MinValue);
await auth.LogoutAsync(Fingerprint);
@ -164,7 +167,6 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
[HttpGet("GetRole")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[Authorize]
[CacheMaxAge(0, 0, 1)]
public ActionResult<AuthRoles> GetRole() => Ok(AuthRoles.Admin);
[HttpPost("RenewPassword")]

View File

@ -1,5 +1,5 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Mirea.Api.DataAccess.Application.Cqrs.Campus.Queries.GetCampusBasicInfoList;
using Mirea.Api.DataAccess.Application.Cqrs.Campus.Queries.GetCampusDetails;
@ -20,7 +20,6 @@ public class CampusController(IMediator mediator) : BaseController
/// </summary>
/// <returns>Basic information about campuses.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<List<CampusBasicInfoResponse>>> Get()
{
var result = await mediator.Send(new GetCampusBasicInfoListQuery());
@ -41,7 +40,6 @@ public class CampusController(IMediator mediator) : BaseController
/// <param name="id">Campus ID.</param>
/// <returns>Details of the specified campus.</returns>
[HttpGet("{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<CampusDetailsResponse>> GetDetails(int id)

View File

@ -1,5 +1,5 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Mirea.Api.DataAccess.Application.Cqrs.Discipline.Queries.GetDisciplineDetails;
using Mirea.Api.DataAccess.Application.Cqrs.Discipline.Queries.GetDisciplineList;
@ -22,7 +22,6 @@ public class DisciplineController(IMediator mediator) : BaseController
/// <param name="pageSize">Number of items per page.</param>
/// <returns>Paginated list of disciplines.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
public async Task<ActionResult<List<DisciplineResponse>>> Get([FromQuery] int? page, [FromQuery] int? pageSize)
{
@ -47,7 +46,6 @@ public class DisciplineController(IMediator mediator) : BaseController
/// <param name="id">Discipline ID.</param>
/// <returns>Details of the specified discipline.</returns>
[HttpGet("{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<DisciplineResponse>> GetDetails(int id)

View File

@ -1,5 +1,5 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Mirea.Api.DataAccess.Application.Cqrs.Faculty.Queries.GetFacultyDetails;
using Mirea.Api.DataAccess.Application.Cqrs.Faculty.Queries.GetFacultyList;
@ -22,7 +22,6 @@ public class FacultyController(IMediator mediator) : BaseController
/// <param name="pageSize">Number of items per page.</param>
/// <returns>Paginated list of faculties.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
public async Task<ActionResult<List<FacultyResponse>>> Get([FromQuery] int? page, [FromQuery] int? pageSize)
{
@ -48,7 +47,6 @@ public class FacultyController(IMediator mediator) : BaseController
/// <param name="id">Faculty ID.</param>
/// <returns>Details of the specified faculty.</returns>
[HttpGet("{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<FacultyDetailsResponse>> GetDetails(int id)

View File

@ -1,5 +1,5 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Mirea.Api.DataAccess.Application.Cqrs.Group.Queries.GetGroupDetails;
using Mirea.Api.DataAccess.Application.Cqrs.Group.Queries.GetGroupList;
@ -36,7 +36,6 @@ public class GroupController(IMediator mediator) : BaseController
/// <param name="pageSize">The page size for pagination (optional).</param>
/// <returns>A list of groups.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
public async Task<ActionResult<List<GroupResponse>>> Get([FromQuery] int? page, [FromQuery] int? pageSize)
{
@ -63,7 +62,6 @@ public class GroupController(IMediator mediator) : BaseController
/// <param name="id">The ID of the group to retrieve.</param>
/// <returns>Detailed information about the group.</returns>
[HttpGet("{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<GroupDetailsResponse>> GetDetails(int id)
@ -89,7 +87,6 @@ public class GroupController(IMediator mediator) : BaseController
/// <param name="id">The ID of the faculty.</param>
/// <returns>A list of groups belonging to the specified faculty.</returns>
[HttpGet("GetByFaculty/{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<List<GroupResponse>>> GetByFaculty(int id)

View File

@ -1,5 +1,5 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Mirea.Api.DataAccess.Application.Cqrs.LectureHall.Queries.GetLectureHallDetails;
using Mirea.Api.DataAccess.Application.Cqrs.LectureHall.Queries.GetLectureHallList;
@ -20,7 +20,6 @@ public class LectureHallController(IMediator mediator) : BaseController
/// </summary>
/// <returns>A list of lecture halls.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<List<LectureHallResponse>>> Get()
{
var result = await mediator.Send(new GetLectureHallListQuery());
@ -41,7 +40,6 @@ public class LectureHallController(IMediator mediator) : BaseController
/// <param name="id">The ID of the lecture hall to retrieve.</param>
/// <returns>The details of the specified lecture hall.</returns>
[HttpGet("{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<LectureHallDetailsResponse>> GetDetails(int id)
@ -67,7 +65,6 @@ public class LectureHallController(IMediator mediator) : BaseController
/// <param name="id">The ID of the campus.</param>
/// <returns>A list of lecture halls in the specified campus.</returns>
[HttpGet("GetByCampus/{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<List<LectureHallResponse>>> GetByCampus(int id)

View File

@ -1,5 +1,5 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Mirea.Api.DataAccess.Application.Cqrs.Professor.Queries.GetProfessorDetails;
using Mirea.Api.DataAccess.Application.Cqrs.Professor.Queries.GetProfessorList;
@ -22,7 +22,6 @@ public class ProfessorController(IMediator mediator) : BaseController
/// <param name="pageSize">The page size for pagination (optional).</param>
/// <returns>A list of professors.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
public async Task<ActionResult<List<ProfessorResponse>>> Get([FromQuery] int? page, [FromQuery] int? pageSize)
{
@ -48,7 +47,6 @@ public class ProfessorController(IMediator mediator) : BaseController
/// <param name="id">The ID of the professor to retrieve.</param>
/// <returns>Detailed information about the professor.</returns>
[HttpGet("{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<ProfessorResponse>> GetDetails(int id)

View File

@ -1,4 +1,5 @@
using MediatR;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
@ -8,7 +9,7 @@ using Mirea.Api.Dto.Requests;
using Mirea.Api.Dto.Responses;
using Mirea.Api.Endpoint.Common.Attributes;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.Model;
using System;
using System.Collections.Generic;
using System.Linq;
@ -34,10 +35,8 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
/// <param name="request">The request object containing filter criteria.</param>
/// <returns>A list of schedules matching the filter criteria.</returns>
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[BadRequestResponse]
[NotFoundResponse]
public async Task<ActionResult<List<ScheduleResponse>>> Get([FromBody] ScheduleRequest request)
{
if ((request.Groups == null || request.Groups.Length == 0) &&
@ -64,7 +63,8 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
ProfessorIds = request.Professors
})).Schedules;
if (result.Count == 0) NoContent();
if (result.Count == 0)
NoContent();
return Ok(result.Select(s => new ScheduleResponse
{
@ -98,7 +98,6 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
/// <param name="lectureHalls">An array of lecture hall IDs.</param>
/// <returns>A response containing schedules for the specified group.</returns>
[HttpGet("GetByGroup/{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[BadRequestResponse]
[NotFoundResponse]
@ -126,7 +125,6 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
/// <param name="lectureHalls">An array of lecture hall IDs.</param>
/// <returns>A response containing schedules for the specified professor.</returns>
[HttpGet("GetByProfessor/{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[BadRequestResponse]
[NotFoundResponse]
@ -154,7 +152,6 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
/// <param name="groups">An array of group IDs.</param>
/// <returns>A response containing schedules for the specified lecture hall.</returns>
[HttpGet("GetByLectureHall/{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[BadRequestResponse]
[NotFoundResponse]
@ -182,7 +179,6 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
/// <param name="lectureHalls">An array of lecture hall IDs.</param>
/// <returns>A response containing schedules for the specified discipline.</returns>
[HttpGet("GetByDiscipline/{id:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[BadRequestResponse]
[NotFoundResponse]

View File

@ -5,9 +5,9 @@
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<Company>Winsomnia</Company>
<Version>1.0.0-rc0</Version>
<AssemblyVersion>1.0.2.0</AssemblyVersion>
<FileVersion>1.0.2.0</FileVersion>
<Version>1.0.0-rc2</Version>
<AssemblyVersion>1.0.2.2</AssemblyVersion>
<FileVersion>1.0.2.2</FileVersion>
<AssemblyName>Mirea.Api.Endpoint</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<OutputType>Exe</OutputType>
@ -22,22 +22,38 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="Cronos" Version="0.8.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PackageReference Include="EPPlus" Version="7.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.11.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.1.2" />
<PackageReference Include="Mirea.Tools.Schedule.WebParser" Version="1.0.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Versioning" Version="2.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.1" />
<PackageReference Include="System.CodeDom" Version="8.0.0" />
<PackageReference Include="System.Composition" Version="8.0.0" />
<PackageReference Include="System.Composition.TypedParts" Version="8.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.2" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
<PackageReference Include="Z.EntityFramework.Extensions.EFCore" Version="8.103.5" />
</ItemGroup>
<ItemGroup>

View File

@ -10,11 +10,11 @@ using Mirea.Api.DataAccess.Persistence;
using Mirea.Api.DataAccess.Persistence.Common;
using Mirea.Api.Endpoint.Common.Interfaces;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Common.Settings;
using Mirea.Api.Endpoint.Configuration.ApplicationConfiguration;
using Mirea.Api.Endpoint.Configuration.General;
using Mirea.Api.Endpoint.Configuration.General.Validators;
using Mirea.Api.Endpoint.Middleware;
using Mirea.Api.Endpoint.Configuration.Core.Middleware;
using Mirea.Api.Endpoint.Configuration.Core.Startup;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Endpoint.Configuration.Validation;
using Mirea.Api.Endpoint.Configuration.Validation.Validators;
using Mirea.Api.Security.Services;
using System;
using System.IO;
@ -64,6 +64,9 @@ public class Program
policy.AllowAnyMethod();
policy.AllowAnyHeader();
policy.AllowCredentials();
#if DEBUG
policy.WithOrigins("http://localhost:4200");
#endif
});
});
@ -81,6 +84,7 @@ public class Program
{
secretForward.SecretForwardToken = GeneratorKey.GenerateAlphaNumeric(16);
secretForward.SaveSetting();
Console.WriteLine($"For the reverse proxy server to work correctly, use the header: '{secretForward.SecretForwardToken}-X-Forwarded-For'");
}
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
@ -124,16 +128,18 @@ public class Program
}
app.UseCustomSwagger(app.Services);
app.UseHttpsRedirection();
app.UseMiddleware<CustomExceptionHandlerMiddleware>();
app.UseMiddleware<MaintenanceModeMiddleware>();
app.UseMiddleware<CookieAuthorizationMiddleware>();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<JwtRevocationMiddleware>();
app.UseMiddleware<CacheMaxAgeMiddleware>();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -15,7 +15,7 @@
<ItemGroup>
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
</ItemGroup>
</Project>

View File

@ -5,18 +5,18 @@
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<Company>Winsomnia</Company>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.3.0</AssemblyVersion>
<FileVersion>1.0.3.0</FileVersion>
<Version>1.0.1</Version>
<AssemblyVersion>1.0.3.1</AssemblyVersion>
<FileVersion>1.0.3.1</FileVersion>
<AssemblyName>Mirea.Api.DataAccess.Application</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageReference Include="MediatR" Version="12.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="FluentValidation" Version="11.10.0" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.10.0" />
<PackageReference Include="MediatR" Version="12.4.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
</ItemGroup>
<ItemGroup>

View File

@ -13,9 +13,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.10" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.8" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
</ItemGroup>