added profilepicture to user. No functionallity
This commit is contained in:
parent
b213523911
commit
067a3e8d3c
@ -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" />
|
||||||
|
@ -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),
|
||||||
};
|
};
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
62
API/Migrations/20240904110821_addedprofilepicture.Designer.cs
generated
Normal file
62
API/Migrations/20240904110821_addedprofilepicture.Designer.cs
generated
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
API/Migrations/20240904110821_addedprofilepicture.cs
Normal file
29
API/Migrations/20240904110821_addedprofilepicture.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
8
API/Persistence/Services/AppConfiguration.cs
Normal file
8
API/Persistence/Services/AppConfiguration.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace API.Persistence.Services
|
||||||
|
{
|
||||||
|
public class AppConfiguration
|
||||||
|
{
|
||||||
|
public string AccessKey { get; set; }
|
||||||
|
public string SecretKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
48
API/Persistence/Services/R2Service.cs
Normal file
48
API/Persistence/Services/R2Service.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user