added profilepicture to user. No functionallity

This commit is contained in:
LilleBRG 2024-09-04 14:32:53 +02:00
parent b213523911
commit 067a3e8d3c
13 changed files with 213 additions and 10 deletions

View File

@ -11,6 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.402.4" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7" />

View File

@ -82,6 +82,7 @@ namespace API.Application.Users.Commands
CreatedAt = DateTime.UtcNow.AddHours(2), CreatedAt = DateTime.UtcNow.AddHours(2),
UpdatedAt = DateTime.UtcNow.AddHours(2), UpdatedAt = DateTime.UtcNow.AddHours(2),
HashedPassword = hashedPassword, HashedPassword = hashedPassword,
ProfilePicture = "",
RefreshToken = System.Guid.NewGuid().ToString(), RefreshToken = System.Guid.NewGuid().ToString(),
RefreshTokenExpiresAt = DateTime.UtcNow.AddDays(7), RefreshTokenExpiresAt = DateTime.UtcNow.AddDays(7),
}; };

View File

@ -1,5 +1,6 @@
using API.Models; using API.Models;
using API.Persistence.Repositories; using API.Persistence.Repositories;
using API.Persistence.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -8,10 +9,16 @@ namespace API.Application.Users.Commands
public class UpdateUser public class UpdateUser
{ {
private readonly IUserRepository _repository; private readonly IUserRepository _repository;
private readonly R2Service _r2Service;
private readonly string _accessKey;
private readonly string _secretKey;
public UpdateUser(IUserRepository repository) public UpdateUser(IUserRepository repository, AppConfiguration config)
{ {
_repository = repository; _repository = repository;
_accessKey = config.AccessKey;
_secretKey = config.SecretKey;
_r2Service = new R2Service(_accessKey, _secretKey);
} }
public async Task<IActionResult> Handle(UpdateUserDTO updateUserDTO) public async Task<IActionResult> Handle(UpdateUserDTO updateUserDTO)
@ -31,22 +38,41 @@ namespace API.Application.Users.Commands
return new ConflictObjectResult(new { message = "Email is already in use." }); return new ConflictObjectResult(new { message = "Email is already in use." });
} }
} }
if (updateUserDTO.Password != "") if (updateUserDTO.Password != "½")
{ {
if (IsPasswordSecure(updateUserDTO.Password)) if (IsPasswordSecure(updateUserDTO.Password))
{ {
string hashedPassword = BCrypt.Net.BCrypt.HashPassword(updateUserDTO.Password); string hashedPassword = BCrypt.Net.BCrypt.HashPassword(updateUserDTO.Password);
currentUser.HashedPassword = hashedPassword; currentUser.HashedPassword = hashedPassword;
} }
else
{
return new ConflictObjectResult(new { message = "Password is not secure." });
}
} }
if (updateUserDTO.Username != "") if (updateUserDTO.Username != "½")
currentUser.Username = updateUserDTO.Username; currentUser.Username = updateUserDTO.Username;
if (updateUserDTO.Email != "") if (updateUserDTO.Email != "½")
currentUser.Email = updateUserDTO.Email; currentUser.Email = updateUserDTO.Email;
string imageUrl = null;
if (updateUserDTO.ProfilePicture != null && updateUserDTO.ProfilePicture.Length > 0)
{
try
{
using (var fileStream = updateUserDTO.ProfilePicture.OpenReadStream())
{
imageUrl = await _r2Service.UploadToR2(fileStream, "PP" + updateUserDTO.Id);
currentUser.ProfilePicture = imageUrl;
}
}
catch (Exception ex)
{
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
bool success = await _repository.UpdateUserAsync(currentUser); bool success = await _repository.UpdateUserAsync(currentUser);
if (success) if (success)

View File

@ -25,7 +25,8 @@ namespace API.Application.Users.Queries
{ {
Id = user.Id, Id = user.Id,
Email = user.Email, Email = user.Email,
Username = user.Username Username = user.Username,
ProfilePictureURL = user.ProfilePicture,
}).ToList(); }).ToList();
return userDTOs; return userDTOs;
} }

View File

@ -24,7 +24,7 @@ namespace API.Application.Users.Queries
return new ConflictObjectResult(new { message = "No user on given Id" }); return new ConflictObjectResult(new { message = "No user on given Id" });
} }
return new OkObjectResult(new { id = user.Id, email = user.Email, username = user.Username, createdAt = user.CreatedAt }); return new OkObjectResult(new { id = user.Id, email = user.Email, username = user.Username, profilePictureURL = user.ProfilePicture, createdAt = user.CreatedAt });
} }

View File

@ -12,6 +12,7 @@ using System.Text.RegularExpressions;
using Helpers; using Helpers;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using API.Persistence.Repositories; using API.Persistence.Repositories;
using API.Persistence.Services;
namespace API.Controllers namespace API.Controllers
{ {
@ -26,6 +27,8 @@ namespace API.Controllers
private readonly DeleteUser _deleteUser; private readonly DeleteUser _deleteUser;
private readonly LoginUser _loginUser; private readonly LoginUser _loginUser;
private readonly TokenHelper _tokenHelper; private readonly TokenHelper _tokenHelper;
private readonly IUserRepository _repository; private readonly IUserRepository _repository;
public UsersController( public UsersController(
@ -36,7 +39,8 @@ namespace API.Controllers
DeleteUser deleteUser, DeleteUser deleteUser,
LoginUser loginUser, LoginUser loginUser,
TokenHelper tokenHelper, TokenHelper tokenHelper,
IUserRepository repository) IUserRepository repository
)
{ {
_queryAllUsers = queryAllUsers; _queryAllUsers = queryAllUsers;
_queryUserById = queryUserById; _queryUserById = queryUserById;
@ -46,6 +50,8 @@ namespace API.Controllers
_loginUser = loginUser; _loginUser = loginUser;
_tokenHelper = tokenHelper; _tokenHelper = tokenHelper;
_repository = repository; _repository = repository;
} }
[HttpPost("login")] [HttpPost("login")]
@ -69,7 +75,7 @@ namespace API.Controllers
[Authorize] [Authorize]
[HttpPut] [HttpPut]
public async Task<IActionResult> PutUser(UpdateUserDTO UpdateUserDTO) public async Task<IActionResult> PutUser([FromForm] UpdateUserDTO UpdateUserDTO)
{ {
return await _updateUser.Handle(UpdateUserDTO); return await _updateUser.Handle(UpdateUserDTO);
} }

View File

@ -0,0 +1,62 @@
// <auto-generated />
using System;
using API;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace API.Migrations
{
[DbContext(typeof(AppDBContext))]
[Migration("20240904110821_addedprofilepicture")]
partial class addedprofilepicture
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.7");
modelBuilder.Entity("API.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasColumnType("TEXT");
b.Property<string>("HashedPassword")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ProfilePicture")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RefreshToken")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("RefreshTokenExpiresAt")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Username")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace API.Migrations
{
/// <inheritdoc />
public partial class addedprofilepicture : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ProfilePicture",
table: "Users",
type: "TEXT",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ProfilePicture",
table: "Users");
}
}
}

View File

@ -32,6 +32,10 @@ namespace API.Migrations
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("ProfilePicture")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RefreshToken") b.Property<string>("RefreshToken")
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");

View File

@ -8,6 +8,7 @@ public class User : BaseModel
public string? Username { get; set; } public string? Username { get; set; }
public string HashedPassword { get; set; } public string HashedPassword { get; set; }
public string RefreshToken { get; set; } public string RefreshToken { get; set; }
public string ProfilePicture { get; set; }
public DateTime RefreshTokenExpiresAt { get; set; } public DateTime RefreshTokenExpiresAt { get; set; }
} }
@ -16,6 +17,8 @@ public class UserDTO
public string Id { get; set; } public string Id { get; set; }
public string Email { get; set; } public string Email { get; set; }
public string Username { get; set; } public string Username { get; set; }
public string ProfilePictureURL { get; set; }
} }
public class LoginDTO public class LoginDTO
@ -37,6 +40,8 @@ public class UpdateUserDTO
public string Email { get; set; } public string Email { get; set; }
public string Username { get; set; } public string Username { get; set; }
public string Password { get; set; } public string Password { get; set; }
public IFormFile ProfilePicture { get; set; }
} }
public class RefreshTokenDTO public class RefreshTokenDTO

View File

@ -0,0 +1,8 @@
namespace API.Persistence.Services
{
public class AppConfiguration
{
public string AccessKey { get; set; }
public string SecretKey { get; set; }
}
}

View File

@ -0,0 +1,48 @@
using Amazon.Runtime;
using Amazon.S3.Model;
using Amazon.S3;
namespace API.Persistence.Services
{
public class R2Service
{
private readonly IAmazonS3 _s3Client;
public string AccessKey { get; }
public string SecretKey { get; }
public R2Service(string accessKey, string secretKey)
{
AccessKey = accessKey;
SecretKey = secretKey;
var credentials = new BasicAWSCredentials(accessKey, secretKey);
var config = new AmazonS3Config
{
ServiceURL = "https://a6051dbbe0af70488aff47b9f4d9fc1c.r2.cloudflarestorage.com",
ForcePathStyle = true
};
_s3Client = new AmazonS3Client(credentials, config);
}
public async Task<string> UploadToR2(Stream fileStream, string fileName)
{
var request = new PutObjectRequest
{
InputStream = fileStream,
BucketName = "h4fil",
Key = fileName,
DisablePayloadSigning = true
};
var response = await _s3Client.PutObjectAsync(request);
if (response.HttpStatusCode != System.Net.HttpStatusCode.OK)
{
throw new AmazonS3Exception($"Error uploading file to S3. HTTP Status Code: {response.HttpStatusCode}");
}
var imageUrl = $"https://h4file.magsapi.com/{fileName}";
return imageUrl;
}
}
}

View File

@ -7,6 +7,7 @@ using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using System.Text; using System.Text;
using Helpers; using Helpers;
using API.Persistence.Services;
namespace API namespace API
{ {
@ -69,6 +70,8 @@ namespace API
}; };
}); });
// Swagger does not by default allow to use Bearer tokens // Swagger does not by default allow to use Bearer tokens
// The method AddSwaggerGen with the following options grants access to address a Bearer token - // The method AddSwaggerGen with the following options grants access to address a Bearer token -
// Simply by clicking the Lock icon and pasting the Bearer Token // Simply by clicking the Lock icon and pasting the Bearer Token
@ -105,6 +108,15 @@ namespace API
Console.WriteLine("Connecting to database with connection string: " + connectionString); Console.WriteLine("Connecting to database with connection string: " + connectionString);
var accessKey = Configuration["AccessKey"] ?? Environment.GetEnvironmentVariable("ACCESS_KEY");
var secretKey = Configuration["SecretKey"] ?? Environment.GetEnvironmentVariable("SECRET_KEY");
builder.Services.AddSingleton(new AppConfiguration
{
AccessKey = accessKey,
SecretKey = secretKey
});
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.