From c86c643f76af79de5be9f0bcd665f5476799d535 Mon Sep 17 00:00:00 2001 From: Adiv <asifadiv@gmail.com> Date: Tue, 11 Apr 2023 16:23:17 +0100 Subject: [PATCH] added image storing --- AuthenticationMicroservice.csproj | 75 +++++++++++++------------ Constants.cs | 5 ++ Controllers/AuthenticationController.cs | 15 ++++- Helpers/ResponseOperationFilter.cs | 19 ++----- Middleware/ExceptionMiddleware.cs | 9 +-- Middleware/SwaggerAuthMiddleware.cs | 2 +- Models/AuthenticationDbContext.cs | 6 +- Program.cs | 11 ++-- Repositories/BaseRepository.cs | 8 ++- Services/AuthService.cs | 5 +- Services/EmailService.cs | 3 +- Services/StorageService.cs | 71 +++++++++++++++++++++++ Services/UserService.cs | 25 +++++---- Settings/ConnectionStrings.cs | 7 +++ appsettings.json | 21 ++++++- 15 files changed, 201 insertions(+), 81 deletions(-) create mode 100644 Services/StorageService.cs create mode 100644 Settings/ConnectionStrings.cs diff --git a/AuthenticationMicroservice.csproj b/AuthenticationMicroservice.csproj index 174ada9..000f7fb 100644 --- a/AuthenticationMicroservice.csproj +++ b/AuthenticationMicroservice.csproj @@ -1,41 +1,44 @@ <Project Sdk="Microsoft.NET.Sdk.Web"> - <PropertyGroup> - <TargetFramework>net8.0</TargetFramework> - <Nullable>enable</Nullable> - <ImplicitUsings>enable</ImplicitUsings> - <UserSecretsId>73bbe79f-c176-4d01-8185-46859357e82a</UserSecretsId> - <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> - <DockerfileContext>.</DockerfileContext> - </PropertyGroup> + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <Nullable>enable</Nullable> + <ImplicitUsings>enable</ImplicitUsings> + <UserSecretsId>73bbe79f-c176-4d01-8185-46859357e82a</UserSecretsId> + <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> + <DockerfileContext>.</DockerfileContext> + </PropertyGroup> - <ItemGroup> - <PackageReference Include="AutoMapper" Version="12.0.1" /> - <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" /> - <PackageReference Include="BenchmarkDotNet" Version="0.13.5" /> - <PackageReference Include="LazyCache" Version="2.4.0" /> - <PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" /> - <PackageReference Include="MailKit" Version="3.6.0" /> - <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0-preview.2.23153.2" /> - <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.0-preview.2.23153.2" /> - <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0-preview.1.23112.2" /> - <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0-preview.2.23128.3" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.2.23128.3"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0-preview.2.23128.3" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.2.23128.3" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-preview.2.23128.3"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" /> - <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> - <PackageReference Include="Polly" Version="7.2.3" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> - <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" /> - <PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" /> - </ItemGroup> + <ItemGroup> + <PackageReference Include="AutoMapper" Version="12.0.1"/> + <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0"/> + <PackageReference Include="Azure.Storage.Blobs" Version="12.16.0-beta.1"/> + <PackageReference Include="Azure.Storage.Common" Version="12.15.0-beta.1"/> + <PackageReference Include="BenchmarkDotNet" Version="0.13.5"/> + <PackageReference Include="LazyCache" Version="2.4.0"/> + <PackageReference Include="LazyCache.AspNetCore" Version="2.4.0"/> + <PackageReference Include="MailKit" Version="3.6.0"/> + <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0-preview.2.23153.2"/> + <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.0-preview.2.23153.2"/> + <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0-preview.1.23112.2"/> + <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0-preview.2.23128.3"/> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.2.23128.3"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0-preview.2.23128.3"/> + <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.2.23128.3"/> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-preview.2.23128.3"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1"/> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/> + <PackageReference Include="Polly" Version="7.2.3"/> + <PackageReference Include="SixLabors.ImageSharp" Version="3.0.1"/> + <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/> + <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0"/> + <PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0"/> + </ItemGroup> </Project> diff --git a/Constants.cs b/Constants.cs index f5bc0e9..ac4c523 100644 --- a/Constants.cs +++ b/Constants.cs @@ -19,6 +19,11 @@ public abstract class Constants public const string ProfileSasToken = "profileSasToken"; } + public static class AzureBlobContainers + { + public const string ProfilePictures = "profile-pictures"; + } + public static class AuthorizationPolicies { public const string HasUserId = "HasUserId"; diff --git a/Controllers/AuthenticationController.cs b/Controllers/AuthenticationController.cs index 49dccd8..179127f 100644 --- a/Controllers/AuthenticationController.cs +++ b/Controllers/AuthenticationController.cs @@ -18,11 +18,11 @@ using Swashbuckle.AspNetCore.Annotations; [ApiController] public class AuthController : BaseAuthController { + private readonly IAppCache _memoryCache; private readonly IAuthService _authService; + private readonly IUserService _userService; private readonly IEmailService _emailService; - private readonly IAppCache _memoryCache; private readonly IRefreshTokenService _refreshTokenService; - private readonly IUserService _userService; public AuthController(IAppCache memoryCache, IAuthService authService, IRefreshTokenService refreshTokenService, IUserService userService, IEmailService emailService) @@ -100,6 +100,8 @@ public class AuthController : BaseAuthController { try { + if (string.IsNullOrWhiteSpace(request.Password) || string.IsNullOrWhiteSpace(request.ConfirmPassword)) + return BadRequest("Please enter a password."); SecurityHelper.EnsurePasswordComplexity(request.Password, request.ConfirmPassword); await _authService.SetPassword(request); } @@ -252,4 +254,13 @@ public class AuthController : BaseAuthController return Ok(null); } + + [HttpPost("UploadProfilePicture")] + [Consumes("multipart/form-data")] + public async Task<ActionResult<ResponseEnvelope<string>>> UploadProfilePicture(IFormFile? profilePicture) + { + if (profilePicture == null) + return BadRequest("Please upload a proper image."); + return Ok(await _userService.UploadProfilePicture(UserId, profilePicture)); + } } \ No newline at end of file diff --git a/Helpers/ResponseOperationFilter.cs b/Helpers/ResponseOperationFilter.cs index 4ade9a3..7b09a26 100644 --- a/Helpers/ResponseOperationFilter.cs +++ b/Helpers/ResponseOperationFilter.cs @@ -28,8 +28,7 @@ public class ResponseOperationFilter : IOperationFilter operation.Responses.Add("403", new OpenApiResponse {Description = "Forbidden"}); if (authAttributes != null && authorizeAttributes.Any() && anonAttributes != null && !anonAttributes.Any()) - { - operation.Security.Add(new OpenApiSecurityRequirement() + operation.Security.Add(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme @@ -38,12 +37,11 @@ public class ResponseOperationFilter : IOperationFilter { Id = "Bearer", Type = ReferenceType.SecurityScheme - }, + } }, new List<string>() } }); - } var apiTokenAttributes = context.MethodInfo.DeclaringType?.GetCustomAttributes(true) .Union(context.MethodInfo.GetCustomAttributes(true)) @@ -59,12 +57,11 @@ public class ResponseOperationFilter : IOperationFilter return; #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. - Constants.Roles[] roles = (Constants.Roles[]) roleField.GetValue(apiTokenAttributes.First()); + var roles = (Constants.Roles[]) roleField.GetValue(apiTokenAttributes.First()); #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. if (roles != null && roles.Contains(Constants.Roles.Api)) - { - operation.Security.Add(new OpenApiSecurityRequirement() + operation.Security.Add(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme @@ -73,17 +70,13 @@ public class ResponseOperationFilter : IOperationFilter { Id = "Authentication-API-Key", Type = ReferenceType.SecurityScheme - }, + } }, new List<string>() } }); - } - if (roles != null) - { - operation.Description += "Required Roles: " + string.Join(", ", roles); - } + if (roles != null) operation.Description += "Required Roles: " + string.Join(", ", roles); } } } \ No newline at end of file diff --git a/Middleware/ExceptionMiddleware.cs b/Middleware/ExceptionMiddleware.cs index 0cdee17..b181b7c 100644 --- a/Middleware/ExceptionMiddleware.cs +++ b/Middleware/ExceptionMiddleware.cs @@ -2,6 +2,7 @@ namespace AuthenticationMicroservice.Middleware; using System.Net; using Exceptions; +using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; public class ExceptionMiddleware @@ -48,7 +49,7 @@ public class ExceptionMiddleware logger.LogWarning(ex.Message); break; - case Microsoft.EntityFrameworkCore.DbUpdateException dbex: + case DbUpdateException dbex: envelope = new ResponseEnvelope<object>(dbex, userMessage: "Database Error has occurred. Please try again or report the issue."); context.Response.StatusCode = (int) HttpStatusCode.InternalServerError; @@ -85,8 +86,6 @@ public class ResponseEnvelope public class ResponseEnvelope<T> : ResponseEnvelope { - public new T? Data { get; set; } - public ResponseEnvelope() { } @@ -94,7 +93,7 @@ public class ResponseEnvelope<T> : ResponseEnvelope public ResponseEnvelope(Exception ex, int statusCode = 400, string? userMessage = null) : this( new List<ErrorDetails> { - new ErrorDetails + new() { Message = ex.Message, InnerMessage = ex.InnerException?.Message, @@ -125,6 +124,8 @@ public class ResponseEnvelope<T> : ResponseEnvelope StatusCode = statusCode; } + public new T? Data { get; set; } + public override string ToString() { return JsonConvert.SerializeObject(this); diff --git a/Middleware/SwaggerAuthMiddleware.cs b/Middleware/SwaggerAuthMiddleware.cs index fd3f5b5..0ed8341 100644 --- a/Middleware/SwaggerAuthMiddleware.cs +++ b/Middleware/SwaggerAuthMiddleware.cs @@ -9,7 +9,7 @@ public class SwaggerAuthMiddleware public SwaggerAuthMiddleware(RequestDelegate next) { - this._next = next; + _next = next; } public async Task InvokeAsync(HttpContext context) diff --git a/Models/AuthenticationDbContext.cs b/Models/AuthenticationDbContext.cs index 0fcf147..b720a49 100644 --- a/Models/AuthenticationDbContext.cs +++ b/Models/AuthenticationDbContext.cs @@ -9,11 +9,13 @@ public class AuthenticationDbContext : DbContext { public static string ConnectionStringName = "DbConnectionString"; - public AuthenticationDbContext(DbContextOptions<AuthenticationDbContext> options) : base(options) { } + public AuthenticationDbContext(DbContextOptions<AuthenticationDbContext> options) : base(options) + { + } public DbSet<User> User { get; set; } = null!; public DbSet<RefreshToken> RefreshToken { get; set; } = null!; - + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); diff --git a/Program.cs b/Program.cs index 1fd84ed..b99b655 100644 --- a/Program.cs +++ b/Program.cs @@ -24,8 +24,9 @@ builder.Configuration.AddJsonFile("appsettings.Development.json", true, true); builder.Services.Configure<EmailConfig>(builder.Configuration.GetSection("EmailConfig")); builder.Services.Configure<TokenSettings>(builder.Configuration.GetSection("TokenSettings")); builder.Services.AddDbContext<AuthenticationDbContext>(opt => - opt.UseSqlServer(builder.Configuration.GetConnectionString("AuthenticationDb") ?? + opt.UseSqlServer(builder.Configuration.GetConnectionString("DbConnectionString") ?? throw new Exception("Could not connect to database."))); +builder.Services.Configure<ConnectionStrings>(builder.Configuration.GetSection("ConnectionStrings")); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "AuthenticationMicroserviceAPI", Version = "v1"}); @@ -104,8 +105,11 @@ var mapperConfig = new MapperConfiguration(mc => { mc.AddProfile(new AutoMapperP var mapper = mapperConfig.CreateMapper(); builder.Services.AddSingleton(mapper); +// Add repositories from /Repositories builder.Services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>)); +// Add services from /Services +builder.Services.AddTransient<IStorageService, StorageService>(); builder.Services.AddTransient<IUserService, UserService>(); builder.Services.AddTransient<IRefreshTokenService, RefreshTokenService>(); builder.Services.AddTransient<IEmailService, EmailService>(); @@ -143,9 +147,6 @@ app.UseMiddleware<ExceptionMiddleware>(); app.UseHttpsRedirection(); app.UseOutputCache(); -app.UseEndpoints(endpoints => -{ - endpoints.MapControllers(); -}); +app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.Run(); \ No newline at end of file diff --git a/Repositories/BaseRepository.cs b/Repositories/BaseRepository.cs index 4d3cbe1..ace3136 100644 --- a/Repositories/BaseRepository.cs +++ b/Repositories/BaseRepository.cs @@ -26,8 +26,8 @@ public interface IBaseRepository<TEntity> where TEntity : class public sealed class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class { - private readonly AuthenticationDbContext _context; private readonly DbSet<TEntity> _dbSet; + private readonly AuthenticationDbContext _context; public BaseRepository(AuthenticationDbContext context) { @@ -47,12 +47,14 @@ public sealed class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEn public TEntity GetByIdThrowIfNull(int id) { - return GetById(id) ?? throw new AuthenticationException($"Item with ID {id} does not exist.", HttpStatusCode.NotFound); + return GetById(id) ?? + throw new AuthenticationException($"Item with ID {id} does not exist.", HttpStatusCode.NotFound); } public TEntity GetByIdThrowIfNull(Guid id) { - return GetById(id) ?? throw new AuthenticationException($"Item with ID {id} does not exist.", HttpStatusCode.NotFound); + return GetById(id) ?? + throw new AuthenticationException($"Item with ID {id} does not exist.", HttpStatusCode.NotFound); } public IQueryable<TEntity> GetAll() diff --git a/Services/AuthService.cs b/Services/AuthService.cs index a506085..971c509 100644 --- a/Services/AuthService.cs +++ b/Services/AuthService.cs @@ -30,7 +30,8 @@ public class AuthService : IAuthService private readonly AuthenticationDbContext _context; private readonly IRefreshTokenService _refreshTokenService; - public AuthService(AuthenticationDbContext context, IEmailService emailService, IRefreshTokenService refreshTokenService) + public AuthService(AuthenticationDbContext context, IEmailService emailService, + IRefreshTokenService refreshTokenService) { _context = context; _emailService = emailService; @@ -62,7 +63,7 @@ public class AuthService : IAuthService throw new AuthenticationException("User not found.", HttpStatusCode.Unauthorized); return await GenerateAuthenticatedUser(user, oldRefreshToken); } - + public async Task<AuthenticatedUserDTO> GenerateAuthenticatedUser(User user, string? oldToken = null) { var newAccess = _refreshTokenService.CreateNewTokenForUser(user); diff --git a/Services/EmailService.cs b/Services/EmailService.cs index fe24bd4..e19d1bf 100644 --- a/Services/EmailService.cs +++ b/Services/EmailService.cs @@ -41,7 +41,8 @@ public class EmailService : IEmailService } catch (Exception ex) { - throw new AuthenticationException($"Failed to send email to: {to}. " + ex.Message, HttpStatusCode.InternalServerError); + throw new AuthenticationException($"Failed to send email to: {to}. " + ex.Message, + HttpStatusCode.InternalServerError); } } } \ No newline at end of file diff --git a/Services/StorageService.cs b/Services/StorageService.cs new file mode 100644 index 0000000..b01487e --- /dev/null +++ b/Services/StorageService.cs @@ -0,0 +1,71 @@ +namespace AuthenticationMicroservice.Services; + +using System.Web; +using Azure.Storage.Blobs; +using Azure.Storage.Blobs.Models; +using Azure.Storage.Sas; +using Microsoft.Extensions.Options; +using Settings; + +public interface IStorageService +{ + Task<Uri> SaveImageAsJpgBlob(string containerName, string fileName, Stream input, + Dictionary<string, string>? metadata = null); + + string GetSasForFile(string containerName, string url, DateTimeOffset? expires = null); +} + +public class StorageService : IStorageService +{ + private readonly ConnectionStrings _connectionStrings; + + public StorageService(IOptions<ConnectionStrings> connectionStrings) + { + _connectionStrings = connectionStrings.Value; + } + + public string GetSasForFile(string containerName, string url, DateTimeOffset? expires = null) + { + url = GetFileFromUrl(url, containerName) ?? string.Empty; + if (string.IsNullOrWhiteSpace(url)) + return string.Empty; + var container = new BlobContainerClient(_connectionStrings.AzureBlobStorage, containerName); + var blob = container.GetBlobClient(url); + var urlWithSas = blob.GenerateSasUri(BlobSasPermissions.Read, DateTimeOffset.Now.AddHours(3)).ToString(); + return urlWithSas; + } + + public async Task<Uri> SaveImageAsJpgBlob(string containerName, string fileName, Stream input, + Dictionary<string, string>? metadata = null) + { + using var image = await Image.LoadAsync(input); + using var croppedImage = new MemoryStream(); + var clone = image.Clone(context => + context.Resize(new ResizeOptions {Mode = ResizeMode.Max, Size = new Size(1366, 768)})); + await clone.SaveAsJpegAsync(croppedImage); + croppedImage.Position = 0; + var blob = await GetBlobReference(containerName, fileName); + await blob.SetHttpHeadersAsync(new BlobHttpHeaders {ContentType = "image/jpeg"}); + await blob.UploadAsync(croppedImage); + return blob.Uri; + } + + private async Task<BlobClient> GetBlobReference(string? containerName, string blobName) + { + var container = new BlobContainerClient(_connectionStrings.AzureBlobStorage, containerName); + await container.CreateIfNotExistsAsync(); + var blob = container.GetBlobClient(blobName); + return blob; + } + + private static string? GetFileFromUrl(string url, string container) + { + if (string.IsNullOrEmpty(url)) + return null; + + var userDataContainerString = container + "/"; + var fileUrl = + url[(url.IndexOf(userDataContainerString, StringComparison.Ordinal) + userDataContainerString.Length)..]; + return HttpUtility.UrlDecode(fileUrl); + } +} \ No newline at end of file diff --git a/Services/UserService.cs b/Services/UserService.cs index 9a0e566..a329e88 100644 --- a/Services/UserService.cs +++ b/Services/UserService.cs @@ -14,19 +14,20 @@ public interface IUserService { Task<User> CreateUser(UserRegisterRequestDTO request); Task<string> UploadProfilePicture(int userId, IFormFile profilePicture); - Task<string> EditProfilePicture(int userId, IFormFile? profilePicture = null); List<UserDTO> GetListOfAllUsers(); } public class UserService : IUserService { private readonly IMapper _mapper; + private readonly IStorageService _storageService; private readonly IBaseRepository<User> _userRepo; - public UserService(IMapper mapper, IBaseRepository<User> userRepo) + public UserService(IMapper mapper, IBaseRepository<User> userRepo, IStorageService storageService) { _mapper = mapper; _userRepo = userRepo; + _storageService = storageService; } public async Task<User> CreateUser(UserRegisterRequestDTO request) @@ -50,18 +51,22 @@ public class UserService : IUserService return await _userRepo.CreateAndSaveAsync(newUser); } - public Task<string> UploadProfilePicture(int userId, IFormFile profilePicture) + public async Task<string> UploadProfilePicture(int userId, IFormFile profilePicture) { - throw new NotImplementedException(); - } - - public Task<string> EditProfilePicture(int userId, IFormFile? profilePicture = null) - { - throw new NotImplementedException(); + var user = _userRepo.GetByIdThrowIfNull(userId); + using var ms = new MemoryStream(); + await profilePicture.CopyToAsync(ms); + ms.Position = 0; + var fileName = $"{user.Id}/{user.FirstName}"; + var image = await _storageService.SaveImageAsJpgBlob(Constants.AzureBlobContainers.ProfilePictures, fileName, + ms); + user.ProfilePictureUrl = image.ToString(); + await _userRepo.UpdateAndSaveAsync(user); + return _storageService.GetSasForFile(Constants.AzureBlobContainers.ProfilePictures, image.ToString()); } public List<UserDTO> GetListOfAllUsers() { - return _mapper.ProjectTo<UserDTO>(_userRepo.GetAll()).ToList(); + return _mapper.ProjectTo<UserDTO>(_userRepo.GetAll().AsNoTracking()).ToList(); } } \ No newline at end of file diff --git a/Settings/ConnectionStrings.cs b/Settings/ConnectionStrings.cs new file mode 100644 index 0000000..b39afcd --- /dev/null +++ b/Settings/ConnectionStrings.cs @@ -0,0 +1,7 @@ +namespace AuthenticationMicroservice.Settings; + +public class ConnectionStrings +{ + public string? DbConnectionString { get; set; } + public string? AzureBlobStorage { get; set; } +} \ No newline at end of file diff --git a/appsettings.json b/appsettings.json index 10f68b8..bc7d0aa 100644 --- a/appsettings.json +++ b/appsettings.json @@ -5,5 +5,22 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" -} + "AllowedHosts": "*", + "ConnectionStrings": { + "DbConnectionString": "", + "AzureBlobStorage": "" + }, + "TokenSettings": { + "RefreshTokenValidityMinutes": 43200, + "AccessTokenValidityMinutes": 3600, + "RefreshTokenRandomNumbers": 32, + "Secret": "" + }, + "EmailConfig": { + "Email": "", + "Password": "" + }, + "Group17Website": { + "BaseUrl": "" + } +} \ No newline at end of file -- GitLab