chore(codebase): full cleanup pass
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
namespace Hutopy.Infrastructure.BlobStorage.Contracts;
|
||||
|
||||
public static class ContainerNames
|
||||
internal static class ContainerNames
|
||||
{
|
||||
public const string Users = "users";
|
||||
public const string Creators = "creators";
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Hutopy.Infrastructure.BlobStorage.Contracts;
|
||||
public interface IBlobStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// Upload a file to blob storage.
|
||||
/// Upload a file to blob storage.
|
||||
/// </summary>
|
||||
/// <param name="containerName">The name of the container where the file is stored.</param>
|
||||
/// <param name="blobName">The blob name (path within the container, include the file name).</param>
|
||||
@@ -12,14 +12,14 @@ public interface IBlobStorage
|
||||
/// <param name="ct">The cancellation token</param>
|
||||
/// <returns></returns>
|
||||
Task<string> UploadFileAsync(
|
||||
string containerName,
|
||||
string blobName,
|
||||
string containerName,
|
||||
string blobName,
|
||||
Stream stream,
|
||||
string contentType,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Download a file to blob storage.
|
||||
/// Download a file to blob storage.
|
||||
/// </summary>
|
||||
/// <param name="blobName">The blob name (path within the container).</param>
|
||||
/// <param name="containerName">The name of the container where the file is stored. (users)</param>
|
||||
|
||||
@@ -8,19 +8,19 @@ namespace Hutopy.Infrastructure.BlobStorage.Services;
|
||||
public class AzureBlobStorage : IBlobStorage
|
||||
{
|
||||
private const long MaxUploadSize = 10 * 1024 * 1024; // 10 MB in bytes
|
||||
|
||||
|
||||
private readonly BlobServiceClient _blobServiceClient;
|
||||
private readonly ILogger<AzureBlobStorage> _logger;
|
||||
|
||||
public AzureBlobStorage(IConfiguration configuration, ILogger<AzureBlobStorage> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
var connectionString = configuration.GetConnectionString("AzureBlob");
|
||||
string? connectionString = configuration.GetConnectionString("AzureBlob");
|
||||
_blobServiceClient = new BlobServiceClient(connectionString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload a file to microsoft azure blob storage.
|
||||
/// Upload a file to microsoft azure blob storage.
|
||||
/// </summary>
|
||||
/// <param name="containerName">The name of the container where the file is stored.</param>
|
||||
/// <param name="blobName">The blob name (path within the container, include the file name).</param>
|
||||
@@ -29,8 +29,8 @@ public class AzureBlobStorage : IBlobStorage
|
||||
/// <param name="ct">The cancellation token</param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> UploadFileAsync(
|
||||
string containerName,
|
||||
string blobName,
|
||||
string containerName,
|
||||
string blobName,
|
||||
Stream stream,
|
||||
string contentType,
|
||||
CancellationToken ct = default)
|
||||
@@ -59,7 +59,7 @@ public class AzureBlobStorage : IBlobStorage
|
||||
try
|
||||
{
|
||||
// Get a reference to a container
|
||||
var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
|
||||
BlobContainerClient? containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
|
||||
|
||||
// Create the container if it does not exist
|
||||
await containerClient.CreateIfNotExistsAsync(
|
||||
@@ -67,18 +67,18 @@ public class AzureBlobStorage : IBlobStorage
|
||||
cancellationToken: ct);
|
||||
|
||||
// Get a reference to a blob
|
||||
var blobClient = containerClient.GetBlobClient(blobName);
|
||||
BlobClient? blobClient = containerClient.GetBlobClient(blobName);
|
||||
|
||||
// Define the BlobHttpHeaders to include the content type
|
||||
var blobHttpHeaders = new BlobHttpHeaders { ContentType = contentType };
|
||||
BlobHttpHeaders blobHttpHeaders = new() { ContentType = contentType };
|
||||
|
||||
// Upload the file
|
||||
var response = await blobClient.UploadAsync(
|
||||
Response<BlobContentInfo>? response = await blobClient.UploadAsync(
|
||||
stream,
|
||||
new BlobUploadOptions { HttpHeaders = blobHttpHeaders },
|
||||
ct);
|
||||
|
||||
var fileUri = blobClient.Uri.ToString();
|
||||
string fileUri = blobClient.Uri.ToString();
|
||||
|
||||
_logger.LogInformation(
|
||||
"""
|
||||
@@ -112,7 +112,7 @@ public class AzureBlobStorage : IBlobStorage
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download a file to microsoft's azure blob storage.
|
||||
/// Download a file to microsoft's azure blob storage.
|
||||
/// </summary>
|
||||
/// <param name="blobName">The blob name (path within the container).</param>
|
||||
/// <param name="containerName">The name of the container where the file is stored. (users)</param>
|
||||
@@ -126,14 +126,14 @@ public class AzureBlobStorage : IBlobStorage
|
||||
try
|
||||
{
|
||||
// Get a reference to a container
|
||||
var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
|
||||
BlobContainerClient? containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
|
||||
|
||||
// Get a reference to a blob
|
||||
var blobClient = containerClient.GetBlobClient(blobName);
|
||||
BlobClient? blobClient = containerClient.GetBlobClient(blobName);
|
||||
|
||||
// Download the blob to a stream
|
||||
BlobDownloadInfo download = await blobClient.DownloadAsync(ct);
|
||||
|
||||
|
||||
MemoryStream memoryStream = new();
|
||||
await download.Content.CopyToAsync(memoryStream, ct);
|
||||
memoryStream.Position = 0; // Ensure the stream is at the beginning
|
||||
|
||||
@@ -3,6 +3,6 @@ namespace Hutopy.Infrastructure.Configuration;
|
||||
public class WebsiteOptions
|
||||
{
|
||||
public const string SectionName = "Website";
|
||||
|
||||
|
||||
public string FrontendBaseUrl { get; set; } = "https://localhost:5173";
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Hutopy.Infrastructure.Payments.Stripe.Configuration;
|
||||
public class StripeOptions
|
||||
{
|
||||
public const string ConfigurationSection = "Stripe";
|
||||
|
||||
|
||||
[Required] public required string SecretKey { get; init; }
|
||||
|
||||
[Required] public required string WebhookSecret { get; init; }
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Hutopy.Infrastructure.Payments.Stripe.Services;
|
||||
|
||||
public class MembershipPaymentProcessor(
|
||||
IOptions<StripeOptions> stripeOptions)
|
||||
: IMembershipPaymentProcessor
|
||||
: IMembershipPaymentProcessor
|
||||
{
|
||||
public async Task<MembershipCheckoutSession> CreateCheckoutSessionAsync(
|
||||
Guid userId,
|
||||
@@ -22,16 +22,16 @@ public class MembershipPaymentProcessor(
|
||||
StripeConfiguration.ApiKey = stripeOptions.Value.SecretKey;
|
||||
|
||||
// Create Stripe customer for the user if not already created
|
||||
var customerService = new CustomerService();
|
||||
var customer = await customerService.CreateAsync(
|
||||
CustomerService customerService = new();
|
||||
Customer? customer = await customerService.CreateAsync(
|
||||
new CustomerCreateOptions
|
||||
{
|
||||
Metadata = new Dictionary<string, string> { { "userId", userId.ToString() } }
|
||||
});
|
||||
|
||||
// Create Checkout Session for the subscription
|
||||
var sessionService = new SessionService();
|
||||
var session = await sessionService.CreateAsync(
|
||||
SessionService sessionService = new();
|
||||
Session? session = await sessionService.CreateAsync(
|
||||
new SessionCreateOptions
|
||||
{
|
||||
Customer = customer.Id,
|
||||
@@ -44,7 +44,11 @@ public class MembershipPaymentProcessor(
|
||||
SubscriptionData = new SessionSubscriptionDataOptions
|
||||
{
|
||||
ApplicationFeePercent = stripeOptions.Value.HutopyRate,
|
||||
TransferData = new SessionSubscriptionDataTransferDataOptions { Destination = creatorReference.StripeAccountId }
|
||||
TransferData =
|
||||
new SessionSubscriptionDataTransferDataOptions
|
||||
{
|
||||
Destination = creatorReference.StripeAccountId
|
||||
}
|
||||
},
|
||||
SuccessUrl = successUrl, // Redirect after successful payment
|
||||
CancelUrl = cancelUrl, // Redirect after canceled payment
|
||||
@@ -61,5 +65,4 @@ public class MembershipPaymentProcessor(
|
||||
session.Id,
|
||||
session.Url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Stripe;
|
||||
namespace Hutopy.Infrastructure.Payments.Stripe.Services;
|
||||
|
||||
public sealed class MembershipTierProcessor(
|
||||
IOptions<StripeOptions> stripeOptions)
|
||||
IOptions<StripeOptions> stripeOptions)
|
||||
: IMembershipTierProcessor
|
||||
{
|
||||
public async Task<string> CreateAsync(
|
||||
@@ -19,8 +19,8 @@ public sealed class MembershipTierProcessor(
|
||||
StripeConfiguration.ApiKey = stripeOptions.Value.SecretKey;
|
||||
|
||||
// Create the product
|
||||
var productService = new ProductService();
|
||||
var product = await productService.CreateAsync(
|
||||
ProductService productService = new();
|
||||
Product? product = await productService.CreateAsync(
|
||||
new ProductCreateOptions
|
||||
{
|
||||
Name = productName,
|
||||
@@ -28,7 +28,7 @@ public sealed class MembershipTierProcessor(
|
||||
});
|
||||
|
||||
// Create the price for the product
|
||||
var priceService = new PriceService();
|
||||
PriceService priceService = new();
|
||||
await priceService.CreateAsync(
|
||||
new PriceCreateOptions
|
||||
{
|
||||
|
||||
@@ -24,14 +24,14 @@ public class StripeTipProcessor(
|
||||
StripeConfiguration.ApiKey = stripeOptions.Value.SecretKey;
|
||||
|
||||
// Create Stripe customer for the user if not already created
|
||||
var customerService = new CustomerService();
|
||||
var customer = await customerService.CreateAsync(
|
||||
CustomerService customerService = new();
|
||||
Customer? customer = await customerService.CreateAsync(
|
||||
new CustomerCreateOptions(),
|
||||
cancellationToken: ct);
|
||||
|
||||
// Create paymentIntent for the user
|
||||
var sessionService = new SessionService();
|
||||
var session = await sessionService.CreateAsync(
|
||||
SessionService sessionService = new();
|
||||
Session? session = await sessionService.CreateAsync(
|
||||
new SessionCreateOptions
|
||||
{
|
||||
ClientReferenceId = tipId.ToString(),
|
||||
@@ -68,7 +68,7 @@ public class StripeTipProcessor(
|
||||
CancelUrl = cancelUrl, // Redirect after canceled payment
|
||||
Metadata = new Dictionary<string, string>
|
||||
{
|
||||
{ "creatorId", creator.Id.ToString() }, { "creatorName", creator.Name }, { "message", message },
|
||||
{ "creatorId", creator.Id.ToString() }, { "creatorName", creator.Name }, { "message", message }
|
||||
}
|
||||
},
|
||||
cancellationToken: ct);
|
||||
|
||||
@@ -8,22 +8,22 @@ public static class ClaimsPrincipalExtensions
|
||||
{
|
||||
return (Guid)claims.GetRequiredClaim<Guid>(ClaimTypes.NameIdentifier);
|
||||
}
|
||||
|
||||
|
||||
public static string GetName(this ClaimsPrincipal claims)
|
||||
{
|
||||
return (string)claims.GetRequiredClaim<string>(ClaimTypes.Name);
|
||||
}
|
||||
|
||||
|
||||
public static string? GetAlias(this ClaimsPrincipal claims)
|
||||
{
|
||||
return (string?)claims.GetClaim<string?>(KnownClaims.Alias);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static string? GetPortraitUrl(this ClaimsPrincipal claims)
|
||||
{
|
||||
return (string?)claims.GetClaim<string?>(KnownClaims.PortraitUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static string GetFirstName(this ClaimsPrincipal claims)
|
||||
{
|
||||
return (string)claims.GetRequiredClaim<string>(ClaimTypes.GivenName);
|
||||
@@ -41,17 +41,22 @@ public static class ClaimsPrincipalExtensions
|
||||
|
||||
private static object? GetClaim<TValue>(this ClaimsPrincipal claims, string key)
|
||||
{
|
||||
var claim = claims.FindFirst(key);
|
||||
Claim? claim = claims.FindFirst(key);
|
||||
|
||||
return claim is null ? null : claims.GetRequiredClaim<TValue>(key);
|
||||
}
|
||||
|
||||
private static object GetRequiredClaim<TValue>(this ClaimsPrincipal claims, string key)
|
||||
{
|
||||
var claim = claims.FindFirst(key);
|
||||
Claim? claim = claims.FindFirst(key);
|
||||
|
||||
if (claim is null) throw new MissingClaimException(key);
|
||||
if (claim is null)
|
||||
{
|
||||
throw new MissingClaimException(key);
|
||||
}
|
||||
|
||||
return typeof(TValue) == typeof(Guid) ? Guid.Parse(claim.Value) : Convert.ChangeType(claim.Value, typeof(TValue));
|
||||
return typeof(TValue) == typeof(Guid)
|
||||
? Guid.Parse(claim.Value)
|
||||
: Convert.ChangeType(claim.Value, typeof(TValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ public static class JwtTokenHelper
|
||||
string lastname,
|
||||
string? portraitUrl)
|
||||
{
|
||||
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
|
||||
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
|
||||
SymmetricSecurityKey securityKey = new(Encoding.UTF8.GetBytes(key));
|
||||
SigningCredentials credentials = new(securityKey, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var claims = new List<Claim>([
|
||||
List<Claim> claims = new([
|
||||
new Claim(JwtRegisteredClaimNames.Sub, userId),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(ClaimTypes.NameIdentifier, userId), new Claim(ClaimTypes.Email, email),
|
||||
@@ -40,10 +40,10 @@ public static class JwtTokenHelper
|
||||
claims.Add(new Claim(KnownClaims.PortraitUrl, portraitUrl));
|
||||
}
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: issuer,
|
||||
audience: audience,
|
||||
claims: claims,
|
||||
JwtSecurityToken token = new(
|
||||
issuer,
|
||||
audience,
|
||||
claims,
|
||||
expires: DateTime.Now.Add(expiresIn),
|
||||
signingCredentials: credentials);
|
||||
|
||||
|
||||
@@ -21,38 +21,54 @@ public static class PasswordGenerator
|
||||
bool requireSpecialCharacter = true)
|
||||
{
|
||||
// Create pools based on the requirements
|
||||
var characterPool = new StringBuilder();
|
||||
StringBuilder characterPool = new();
|
||||
|
||||
if (requireNumber)
|
||||
{
|
||||
characterPool.Append(LowerLetters);
|
||||
|
||||
}
|
||||
|
||||
if (requireCapital)
|
||||
{
|
||||
characterPool.Append(UpperLetters);
|
||||
}
|
||||
|
||||
if (requireNumber)
|
||||
{
|
||||
characterPool.Append(Numbers);
|
||||
}
|
||||
|
||||
if (requireSpecialCharacter)
|
||||
{
|
||||
characterPool.Append(SpecialCharacters);
|
||||
}
|
||||
|
||||
// Ensure that the length is within the specified bounds
|
||||
var password = new char[length];
|
||||
char[] password = new char[length];
|
||||
|
||||
// Ensure at least one character from each required category is included
|
||||
int index = 0;
|
||||
|
||||
|
||||
if (requireLowercase)
|
||||
{
|
||||
password[index++] = LowerLetters[Random.Next(LowerLetters.Length)];
|
||||
|
||||
}
|
||||
|
||||
if (requireCapital)
|
||||
{
|
||||
password[index++] = UpperLetters[Random.Next(UpperLetters.Length)];
|
||||
|
||||
}
|
||||
|
||||
if (requireNumber)
|
||||
{
|
||||
password[index++] = Numbers[Random.Next(Numbers.Length)];
|
||||
|
||||
}
|
||||
|
||||
if (requireSpecialCharacter)
|
||||
{
|
||||
password[index++] = SpecialCharacters[Random.Next(SpecialCharacters.Length)];
|
||||
|
||||
}
|
||||
|
||||
// Fill the rest with the password
|
||||
for (int i = index; i < length; i++)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ public static class RefreshTokenGenerator
|
||||
{
|
||||
public static string Next()
|
||||
{
|
||||
var randomNumber = new byte[32];
|
||||
byte[] randomNumber = new byte[32];
|
||||
RandomNumberGenerator.Fill(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
|
||||
@@ -13,52 +13,62 @@ public static class YouTubeUrlHelper
|
||||
RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the video ID from a YouTube URL or returns the input if it's already a video ID.
|
||||
/// Extracts the video ID from a YouTube URL or returns the input if it's already a video ID.
|
||||
/// </summary>
|
||||
/// <param name="input">The YouTube URL or video ID</param>
|
||||
/// <returns>The extracted video ID or null if invalid</returns>
|
||||
public static string? ExtractVideoId(string? input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If it's already a valid video ID, return it
|
||||
if (IsValidVideoId(input))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
// Try to extract video ID from URL
|
||||
var match = VideoIdRegex.Match(input);
|
||||
Match match = VideoIdRegex.Match(input);
|
||||
return match.Success ? match.Groups[1].Value : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates if the input is a valid YouTube video ID.
|
||||
/// Validates if the input is a valid YouTube video ID.
|
||||
/// </summary>
|
||||
/// <param name="input">The video ID to validate</param>
|
||||
/// <returns>True if the input is a valid video ID</returns>
|
||||
public static bool IsValidVideoId(string? input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ShortUrlRegex.IsMatch(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates if the input is a valid YouTube URL or video ID.
|
||||
/// Validates if the input is a valid YouTube URL or video ID.
|
||||
/// </summary>
|
||||
/// <param name="input">The URL or video ID to validate</param>
|
||||
/// <returns>True if the input is a valid YouTube URL or video ID</returns>
|
||||
public static bool IsValidYouTubeUrlOrId(string? input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it's a valid video ID
|
||||
if (IsValidVideoId(input))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it's a valid YouTube URL
|
||||
return VideoIdRegex.IsMatch(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user