Merge remote-tracking branch 'origin/Devsights-changes'

This commit is contained in:
2025-01-08 15:06:17 -05:00
14 changed files with 603 additions and 60 deletions

View File

@@ -1,7 +1,7 @@
{ {
"sdk": { "sdk": {
"version": "8.0.0", "version": "8.0.0",
"rollForward": "latestMajor", "rollForward": "latestMinor",
"allowPrerelease": false "allowPrerelease": false
} }
} }

View File

@@ -53,14 +53,14 @@ public class PresentationInfos
[MaxLength(255)] public string PhoneNumber { get; set; } = string.Empty; [MaxLength(255)] public string PhoneNumber { get; set; } = string.Empty;
[MaxLength(255)] public string Email { get; set; } = string.Empty; [MaxLength(255)] public string Email { get; set; } = string.Empty;
[MaxLength(2000)] public string Title { get; set; } = string.Empty; [MaxLength(2000)] public string Title { get; set; } = string.Empty;
[MaxLength(2000)] public string MainImageUrl { get; set; } = string.Empty; [MaxLength(2000)] public string? MainImageUrl { get; set; } = string.Empty;
[MaxLength(10000)] public string MainImageText { get; set; } = string.Empty; [MaxLength(10000)] public string MainImageText { get; set; } = string.Empty;
[MaxLength(10000)] public string MainVideoText { get; set; } = string.Empty; [MaxLength(10000)] public string MainVideoText { get; set; } = string.Empty;
[MaxLength(2000)] public string ImagesSubtitle { get; set; } = string.Empty; [MaxLength(2000)] public string ImagesSubtitle { get; set; } = string.Empty;
[MaxLength(2000)] public string Image1Url { get; set; } = string.Empty; [MaxLength(2000)] public string? Image1Url { get; set; } = string.Empty;
[MaxLength(2000)] public string Image2Url { get; set; } = string.Empty; [MaxLength(2000)] public string? Image2Url { get; set; } = string.Empty;
[MaxLength(2000)] public string Image3Url { get; set; } = string.Empty; [MaxLength(2000)] public string? Image3Url { get; set; } = string.Empty;
[MaxLength(2000)] public string Image4Url { get; set; } = string.Empty; [MaxLength(2000)] public string? Image4Url { get; set; } = string.Empty;
[MaxLength(10000)] public string ImagesText { get; set; } = string.Empty; [MaxLength(10000)] public string ImagesText { get; set; } = string.Empty;
[MaxLength(2000)] public string VideoSubtitle { get; set; } = string.Empty; [MaxLength(2000)] public string VideoSubtitle { get; set; } = string.Empty;
[MaxLength(2000)] public string VideoSubtitleMain { get; set; } = string.Empty; [MaxLength(2000)] public string VideoSubtitleMain { get; set; } = string.Empty;

View File

@@ -1,4 +1,5 @@
using Hutopy.Web.Features.Contents.Data; using Hutopy.Web.Common.BlobStorage;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers; namespace Hutopy.Web.Features.Contents.Handlers;
@@ -8,29 +9,37 @@ public record ChangePresentationInfosRequest(
string? PhoneNumber, string? PhoneNumber,
string? Email, string? Email,
string? Title, string? Title,
string? MainImageUrl,
string? MainImageText, string? MainImageText,
string? MainVideoText, string? MainVideoText,
string? ImagesSubtitle, string? ImagesSubtitle,
string? Image1Url,
string? Image2Url,
string? Image3Url,
string? Image4Url,
string? ImagesText, string? ImagesText,
string? VideoSubtitle, string? VideoSubtitle,
string? VideoSubtitleMain, string? VideoSubtitleMain,
string? VideoUrlMain, string? VideoUrlMain,
string? VideoUrl, string? VideoUrl,
string? VideoText); string? VideoText,
string? MainImageUrl,
string? Image1Url,
string? Image2Url,
string? Image3Url,
string? Image4Url,
IFormFile? MainImage,
IFormFile? Image1,
IFormFile? Image2,
IFormFile? Image3,
IFormFile? Image4);
[PublicAPI]
public class ChangePresentationInfosHandler( public class ChangePresentationInfosHandler(
ContentDbContext context) ContentDbContext context,
AzureBlobStorage blobStorage)
: Endpoint<ChangePresentationInfosRequest> : Endpoint<ChangePresentationInfosRequest>
{ {
public override void Configure() public override void Configure()
{ {
Post("/api/creators/{CreatorId}/presentation-infos"); Post("/api/creators/{CreatorId}/presentation-infos");
Options(o => o.WithTags("Creators")); Options(o => o.WithTags("Creators"));
AllowFileUploads();
} }
public override async Task HandleAsync( public override async Task HandleAsync(
@@ -40,30 +49,67 @@ public class ChangePresentationInfosHandler(
var creator = await context var creator = await context
.Creators .Creators
.Include(c => c.PresentationInfos) .Include(c => c.PresentationInfos)
.SingleAsync( .SingleOrDefaultAsync(
c => c.Id == request.CreatorId, c => c.Id == request.CreatorId,
cancellationToken: ct); cancellationToken: ct);
creator.PresentationInfos.PhoneNumber = request.PhoneNumber ?? ""; if (creator is null)
creator.PresentationInfos.Email = request.Email ?? ""; {
creator.PresentationInfos.Title = request.Title ?? ""; await SendNotFoundAsync(ct);
creator.PresentationInfos.MainImageUrl = request.MainImageUrl ?? ""; return;
creator.PresentationInfos.MainImageText = request.MainImageText ?? ""; }
creator.PresentationInfos.MainVideoText = request.MainVideoText ?? "";
creator.PresentationInfos.ImagesSubtitle = request.ImagesSubtitle ?? ""; async Task<string> UploadFileOrDefaultAsync(
creator.PresentationInfos.Image1Url = request.Image1Url ?? ""; IFormFile? file,
creator.PresentationInfos.Image2Url = request.Image2Url ?? ""; string subDirectory,
creator.PresentationInfos.Image3Url = request.Image3Url ?? ""; string fileName,
creator.PresentationInfos.Image4Url = request.Image4Url ?? ""; string? newUrl)
creator.PresentationInfos.ImagesText = request.ImagesText ?? ""; {
creator.PresentationInfos.VideoSubtitle = request.VideoSubtitle ?? ""; if (newUrl == "")
creator.PresentationInfos.VideoSubtitleMain = request.VideoSubtitleMain ?? ""; return "";
creator.PresentationInfos.VideoUrlMain = request.VideoUrlMain ?? "";
creator.PresentationInfos.VideoUrl = request.VideoUrl ?? ""; if (file != null)
creator.PresentationInfos.VideoText = request.VideoText ?? ""; {
return await blobStorage.UploadFileAsync(
ContainerNames.Creators,
$"{request.CreatorId}/{subDirectory}/{fileName}",
file.OpenReadStream(),
file.ContentType,
ct);
}
return newUrl?.Trim() ?? "";
}
creator.PresentationInfos.MainImageUrl = await UploadFileOrDefaultAsync(
request.MainImage, "Profile", "MainImage", request.MainImageUrl);
creator.PresentationInfos.Image1Url = await UploadFileOrDefaultAsync(
request.Image1, "Profile", "Image1", request.Image1Url);
creator.PresentationInfos.Image2Url = await UploadFileOrDefaultAsync(
request.Image2, "Profile", "Image2", request.Image2Url);
creator.PresentationInfos.Image3Url = await UploadFileOrDefaultAsync(
request.Image3, "Profile", "Image3", request.Image3Url);
creator.PresentationInfos.Image4Url = await UploadFileOrDefaultAsync(
request.Image4, "Profile", "Image4", request.Image4Url);
creator.PresentationInfos.PhoneNumber = request.PhoneNumber?.Trim() ?? "";
creator.PresentationInfos.Email = request.Email?.Trim() ?? "";
creator.PresentationInfos.Title = request.Title?.Trim() ?? "";
creator.PresentationInfos.MainImageText = request.MainImageText?.Trim() ?? "";
creator.PresentationInfos.MainVideoText = request.MainVideoText?.Trim() ?? "";
creator.PresentationInfos.ImagesSubtitle = request.ImagesSubtitle?.Trim() ?? "";
creator.PresentationInfos.ImagesText = request.ImagesText?.Trim() ?? "";
creator.PresentationInfos.VideoSubtitle = request.VideoSubtitle?.Trim() ?? "";
creator.PresentationInfos.VideoSubtitleMain = request.VideoSubtitleMain?.Trim() ?? "";
creator.PresentationInfos.VideoUrlMain = request.VideoUrlMain?.Trim() ?? "";
creator.PresentationInfos.VideoUrl = request.VideoUrl?.Trim() ?? "";
creator.PresentationInfos.VideoText = request.VideoText?.Trim() ?? "";
await context.SaveChangesAsync(ct); await context.SaveChangesAsync(ct);
await SendOkAsync(ct); await SendOkAsync(ct);
} }
} }

View File

@@ -0,0 +1,292 @@
// <auto-generated />
using System;
using Hutopy.Web.Features.Memberships.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Hutopy.Web.Features.Memberships.Data.Migrations
{
[DbContext(typeof(MembershipDbContext))]
[Migration("20241216215210_UpdateSeedData")]
partial class UpdateSeedData
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Membership")
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Creator", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PortraitUrl")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StripeAccountId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Creators", "Membership");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Subscription", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("CURRENT_TIMESTAMP");
b.Property<Guid>("CreatorId")
.HasColumnType("uuid");
b.Property<DateTimeOffset?>("EndDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTimeOffset>("StartDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("StripeSessionId")
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<string>("StripeSubscriptionId")
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<Guid>("TierId")
.HasColumnType("uuid");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.HasIndex("TierId");
b.ToTable("Subscriptions", "Membership");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Tier", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("CURRENT_TIMESTAMP");
b.Property<Guid>("CreatorId")
.HasColumnType("uuid");
b.Property<string>("CurrencyCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(4096)
.HasColumnType("character varying(4096)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<decimal>("Price")
.HasColumnType("numeric");
b.Property<string>("StripePriceId")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("StripeProductId")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.ToTable("Tiers", "Membership");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Tip", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<decimal>("Amount")
.HasColumnType("numeric");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("CURRENT_TIMESTAMP");
b.Property<Guid>("CreatorId")
.HasColumnType("uuid");
b.Property<string>("CreatorName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StripeSessionId")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("TipperId")
.HasColumnType("uuid");
b.Property<string>("TipperName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("TransactionId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("TransactionId");
b.ToTable("Tips", "Membership");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Transaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<decimal>("Amount")
.HasColumnType("numeric");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("CURRENT_TIMESTAMP");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StripeInvoiceUrl")
.HasColumnType("text");
b.Property<Guid?>("SubscriptionId")
.HasColumnType("uuid");
b.Property<DateTime>("Timestamp")
.HasColumnType("timestamp with time zone");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("SubscriptionId");
b.ToTable("Transactions", "Membership");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Subscription", b =>
{
b.HasOne("Hutopy.Web.Features.Memberships.Data.Creator", "Creator")
.WithMany()
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Hutopy.Web.Features.Memberships.Data.Tier", "Tier")
.WithMany("Subscriptions")
.HasForeignKey("TierId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Creator");
b.Navigation("Tier");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Tier", b =>
{
b.HasOne("Hutopy.Web.Features.Memberships.Data.Creator", "Creator")
.WithMany()
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Creator");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Tip", b =>
{
b.HasOne("Hutopy.Web.Features.Memberships.Data.Transaction", "Transaction")
.WithMany()
.HasForeignKey("TransactionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Transaction");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Transaction", b =>
{
b.HasOne("Hutopy.Web.Features.Memberships.Data.Subscription", null)
.WithMany("Transactions")
.HasForeignKey("SubscriptionId");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Subscription", b =>
{
b.Navigation("Transactions");
});
modelBuilder.Entity("Hutopy.Web.Features.Memberships.Data.Tier", b =>
{
b.Navigation("Subscriptions");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Web.Features.Memberships.Data.Migrations
{
/// <inheritdoc />
public partial class UpdateSeedData : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@@ -0,0 +1,52 @@
using Hutopy.Web.Common.Security;
using Hutopy.Web.Features.Memberships.Data;
using Hutopy.Web.Features.Memberships.Infrastructure;
namespace Hutopy.Web.Features.Memberships.Handlers;
[PublicAPI]
public record struct ChangeStripeIdRequest(
string StripeAccountId);
public class ChangeStripeIdHandler(
MembershipDbContext dbContext,
StripeService stripeService)
: Endpoint<ChangeStripeIdRequest>
{
public override void Configure()
{
Post("/api/creators/stripe-account");
Options(o => o.WithTags("Memberships"));
}
public override async Task HandleAsync(
ChangeStripeIdRequest req,
CancellationToken ct)
{
var creatorId = HttpContext.User.GetUserId();
var creator = await dbContext
.Creators
.FindAsync(
[creatorId],
cancellationToken: ct);
if (creator is null)
{
creator = new Creator
{
Id = creatorId,
Name = HttpContext.User.GetAlias() ?? creatorId.ToString()
};
await dbContext.AddAsync(creator, ct);
}
creator.StripeAccountId = req.StripeAccountId;
creator.PortraitUrl = HttpContext.User.GetPortraitUrl();
await dbContext.SaveChangesAsync(ct);
await SendOkAsync(creator.Id, ct);
}
}

View File

@@ -51,6 +51,8 @@ public class SendTipHandler(
{ {
Post("/api/tips"); Post("/api/tips");
Options(o => o.WithTags("Memberships")); Options(o => o.WithTags("Memberships"));
AllowAnonymous();
} }
public override async Task HandleAsync( public override async Task HandleAsync(
@@ -68,8 +70,6 @@ public class SendTipHandler(
} }
var checkoutSession = await stripeService.CreateTipCheckoutSessionAsync( var checkoutSession = await stripeService.CreateTipCheckoutSessionAsync(
User.GetUserId(),
User.GetAlias()!,
creator.Id, creator.Id,
creator.Name, creator.Name,
req.Amount, req.Amount,
@@ -77,7 +77,8 @@ public class SendTipHandler(
req.Message, req.Message,
creator.StripeAccountId, creator.StripeAccountId,
req.CheckoutSuccessUrl, req.CheckoutSuccessUrl,
req.CheckoutCancelledUrl); req.CheckoutCancelledUrl
);
await SendAsync( await SendAsync(
new SendTipResponse("Pending", checkoutSession.Url), new SendTipResponse("Pending", checkoutSession.Url),

View File

@@ -55,8 +55,6 @@ public sealed class StripeService(
} }
public async Task<Session> CreateTipCheckoutSessionAsync( public async Task<Session> CreateTipCheckoutSessionAsync(
Guid tipperId,
string tipperName,
Guid creatorId, Guid creatorId,
string creatorName, string creatorName,
decimal amount, decimal amount,
@@ -72,10 +70,7 @@ public sealed class StripeService(
// Create Stripe customer for the user if not already created // Create Stripe customer for the user if not already created
var customerService = new CustomerService(); var customerService = new CustomerService();
var customer = await customerService.CreateAsync( var customer = await customerService.CreateAsync(
new CustomerCreateOptions new CustomerCreateOptions{},
{
Metadata = new Dictionary<string, string> { { "userId", tipperId.ToString() } }
},
cancellationToken: ct); cancellationToken: ct);
// Create paymentIntent for the user // Create paymentIntent for the user
@@ -116,8 +111,6 @@ public sealed class StripeService(
CancelUrl = cancelUrl, // Redirect after canceled payment CancelUrl = cancelUrl, // Redirect after canceled payment
Metadata = new Dictionary<string, string> Metadata = new Dictionary<string, string>
{ {
{ "tipperId", tipperId.ToString() },
{ "tipperName", tipperName },
{ "creatorId", creatorId.ToString() }, { "creatorId", creatorId.ToString() },
{ "creatorName", creatorName }, { "creatorName", creatorName },
{ "message", message }, { "message", message },

View File

@@ -0,0 +1,70 @@
// <auto-generated />
using System;
using Hutopy.Web.Features.Messages.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Hutopy.Web.Features.Messages.Migrations
{
[DbContext(typeof(MessagingDbContext))]
[Migration("20241217225954_ChangeStripeId")]
partial class ChangeStripeId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Messaging")
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Web.Features.Messages.Data.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("CURRENT_TIMESTAMP");
b.Property<Guid>("CreatedBy")
.HasColumnType("uuid");
b.Property<string>("CreatedByName")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<string>("CreatedByPortraitUrl")
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<Guid?>("ParentId")
.HasColumnType("uuid");
b.Property<Guid>("SubjectId")
.HasColumnType("uuid");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2048)
.HasColumnType("character varying(2048)");
b.HasKey("Id");
b.ToTable("Messages", "Messaging");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Web.Features.Messages.Migrations
{
/// <inheritdoc />
public partial class ChangeStripeId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Value",
schema: "Messaging",
table: "Messages",
type: "character varying(2048)",
maxLength: 2048,
nullable: false,
oldClrType: typeof(string),
oldType: "text");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Value",
schema: "Messaging",
table: "Messages",
type: "text",
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(2048)",
oldMaxLength: 2048);
}
}
}

View File

@@ -18,7 +18,7 @@ namespace Hutopy.Web.Features.Messages.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasDefaultSchema("Messaging") .HasDefaultSchema("Messaging")
.HasAnnotation("ProductVersion", "8.0.4") .HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -54,7 +54,8 @@ namespace Hutopy.Web.Features.Messages.Migrations
b.Property<string>("Value") b.Property<string>("Value")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasMaxLength(2048)
.HasColumnType("character varying(2048)");
b.HasKey("Id"); b.HasKey("Id");

View File

@@ -1,16 +1,19 @@
using Hutopy.Web.Features.Users.Handlers.Models; using Hutopy.Web.Features.Users.Handlers.Models;
using Hutopy.Web.Features.Memberships.Data;
using Hutopy.Web.Features.Memberships.Infrastructure;
namespace Hutopy.Web.Features.Users.Handlers; namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI] [PublicAPI]
public class GetCurrentUserQueryHandler( public class GetCurrentUserQueryHandler(
IdentityService identityService) IdentityService identityService,
MembershipDbContext membershipDbContext)
: EndpointWithoutRequest<UserDto> : EndpointWithoutRequest<UserDto>
{ {
public override void Configure() public override void Configure()
{ {
Get("/api/users/profile"); Get("/api/users/profile");
Options(o => o.WithTags("Users")); Options(o => o.WithTags("Memberships"));
} }
public override async Task HandleAsync( public override async Task HandleAsync(
@@ -26,6 +29,12 @@ public class GetCurrentUserQueryHandler(
var roles = await identityService.GetCurrentUserRolesAsync(); var roles = await identityService.GetCurrentUserRolesAsync();
var stripeId = await membershipDbContext
.Creators
.Where(c => c.Id == userModel.Id)
.Select(c => c.StripeAccountId)
.FirstOrDefaultAsync(cancellationToken);
await SendOkAsync( await SendOkAsync(
new UserDto new UserDto
{ {
@@ -40,6 +49,7 @@ public class GetCurrentUserQueryHandler(
BirthDate = userModel.BirthDate, BirthDate = userModel.BirthDate,
Address = userModel.Address, Address = userModel.Address,
UserRoles = roles, UserRoles = roles,
StripeId = stripeId ?? string.Empty
}, },
cancellationToken); cancellationToken);
} }

View File

@@ -13,4 +13,5 @@ public class UserDto
public string? PhoneNumber { get; init; } public string? PhoneNumber { get; init; }
public DateTime? BirthDate { get; init; } public DateTime? BirthDate { get; init; }
public string? Address { get; init; } public string? Address { get; init; }
public string? StripeId { get; init; }
} }

View File

@@ -1,5 +1,8 @@
using Hutopy.Web.Common; using Hutopy.Web.Common;
using Hutopy.Web.Features.Contents.Data; using Hutopy.Web.Features.Contents.Data;
using ContentCreator = Hutopy.Web.Features.Contents.Data.Creator;
using Hutopy.Web.Features.Memberships.Data;
using MembershipCreator = Hutopy.Web.Features.Memberships.Data.Creator;
using Hutopy.Web.Features.Messages.Data; using Hutopy.Web.Features.Messages.Data;
using Hutopy.Web.Features.Users; using Hutopy.Web.Features.Users;
@@ -16,7 +19,8 @@ public static class WebApplicationExtensions
var seeder = new TestDataSeeder( var seeder = new TestDataSeeder(
scope.ServiceProvider.GetRequiredService<ApplicationUserManager>(), scope.ServiceProvider.GetRequiredService<ApplicationUserManager>(),
scope.ServiceProvider.GetRequiredService<ContentDbContext>(), scope.ServiceProvider.GetRequiredService<ContentDbContext>(),
scope.ServiceProvider.GetRequiredService<MessagingDbContext>()); scope.ServiceProvider.GetRequiredService<MessagingDbContext>(),
scope.ServiceProvider.GetRequiredService<MembershipDbContext>());
await seeder.SeedAsync(); await seeder.SeedAsync();
} }
@@ -25,7 +29,8 @@ public static class WebApplicationExtensions
internal class TestDataSeeder( internal class TestDataSeeder(
ApplicationUserManager userManager, ApplicationUserManager userManager,
ContentDbContext contentContext, ContentDbContext contentContext,
MessagingDbContext messagingContext) MessagingDbContext messagingContext,
MembershipDbContext membershipContext)
{ {
private const string DefaultPassword = "Test123#"; private const string DefaultPassword = "Test123#";
@@ -65,11 +70,23 @@ internal class TestDataSeeder(
await messagingContext.SaveChangesAsync(); await messagingContext.SaveChangesAsync();
} }
// convert to MembershipCreator
var membershipCreator = new MembershipCreator
{
Id = creator.Id,
Name = creator.Name,
StripeAccountId = Guid.NewGuid().ToString(),
PortraitUrl = creator.Images.Logo,
};
await membershipContext.Creators.AddAsync(membershipCreator);
await membershipContext.SaveChangesAsync();
} }
} }
private List<Content> GenerateContent( private List<Content> GenerateContent(
Creator creator, ContentCreator creator,
int contentCount) int contentCount)
{ {
var currentDate = DateTimeOffset.UtcNow; var currentDate = DateTimeOffset.UtcNow;
@@ -191,7 +208,7 @@ internal class TestDataSeeder(
[ [
]; ];
private readonly static Creator HutopyCreator = new() private readonly static ContentCreator HutopyCreator = new()
{ {
Name = "hutopy", Name = "hutopy",
Title = "Page officielle", Title = "Page officielle",
@@ -222,7 +239,7 @@ internal class TestDataSeeder(
} }
}; };
private readonly static Creator ArpsCreator = new() private readonly static ContentCreator ArpsCreator = new()
{ {
Name = "arps", Name = "arps",
Title = "Créateur de contenu", Title = "Créateur de contenu",
@@ -255,7 +272,7 @@ internal class TestDataSeeder(
} }
}; };
private readonly static Creator ChloeBeaugrandCreator = new() private readonly static ContentCreator ChloeBeaugrandCreator = new()
{ {
Name = "chloebeaugrand", Name = "chloebeaugrand",
Title = "Page officielle", Title = "Page officielle",
@@ -285,7 +302,7 @@ internal class TestDataSeeder(
} }
}; };
private readonly static Creator GuillaumeMCreator = new() private readonly static ContentCreator GuillaumeMCreator = new()
{ {
Name = "guillaumem", Name = "guillaumem",
Title = "Page officielle", Title = "Page officielle",
@@ -317,7 +334,7 @@ internal class TestDataSeeder(
} }
}; };
private readonly static Creator LeffetCreator = new() private readonly static ContentCreator LeffetCreator = new()
{ {
Name = "leffet", Name = "leffet",
Title = "Page officielle", Title = "Page officielle",
@@ -348,7 +365,7 @@ internal class TestDataSeeder(
} }
}; };
private readonly static Creator MathieuCaron = new() private readonly static ContentCreator MathieuCaron = new()
{ {
Name = "mathieucaron", Name = "mathieucaron",
Title = "Page officielle", Title = "Page officielle",
@@ -378,7 +395,7 @@ internal class TestDataSeeder(
} }
}; };
private readonly Creator[] _creators = private readonly ContentCreator[] _creators =
[ [
HutopyCreator, HutopyCreator,
ArpsCreator, ArpsCreator,