Adds facebook login.
This commit is contained in:
308
backend/src/Web/Features/Users/Data/Migrations/20250217034117_Add_FacebookId.Designer.cs
generated
Normal file
308
backend/src/Web/Features/Users/Data/Migrations/20250217034117_Add_FacebookId.Designer.cs
generated
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Hutopy.Web.Features.Users.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.Users.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(IdentityDbContext))]
|
||||||
|
[Migration("20250217034117_Add_FacebookId")]
|
||||||
|
partial class Add_FacebookId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasDefaultSchema("Identity")
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.10")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hutopy.Web.Features.Users.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", "Identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hutopy.Web.Features.Users.IdentityUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Address")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Alias")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("BirthDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("FacebookId")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Firstname")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("GoogleId")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Lastname")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("PortraitUrl")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", "Identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", "Identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", "Identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", "Identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", "Identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", "Identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Web.Features.Users.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Web.Features.Users.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Web.Features.Users.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Web.Features.Users.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Hutopy.Web.Features.Users.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Web.Features.Users.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Features.Users.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Add_FacebookId : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "FacebookId",
|
||||||
|
schema: "Identity",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "character varying(255)",
|
||||||
|
maxLength: 255,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "FacebookId",
|
||||||
|
schema: "Identity",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,10 @@ namespace Hutopy.Web.Features.Users.Data.Migrations
|
|||||||
b.Property<bool>("EmailConfirmed")
|
b.Property<bool>("EmailConfirmed")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("FacebookId")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
b.Property<string>("Firstname")
|
b.Property<string>("Firstname")
|
||||||
.HasMaxLength(255)
|
.HasMaxLength(255)
|
||||||
.HasColumnType("character varying(255)");
|
.HasColumnType("character varying(255)");
|
||||||
|
|||||||
132
backend/src/Web/Features/Users/Handlers/LoginWithFacebook.cs
Normal file
132
backend/src/Web/Features/Users/Handlers/LoginWithFacebook.cs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Hutopy.Web.Common.Security;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Features.Users.Handlers;
|
||||||
|
|
||||||
|
public class FacebookUserInfo
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")] public required string Id { get; init; }
|
||||||
|
[JsonPropertyName("email")] public string? Email { get; init; } // Email might be null if not granted
|
||||||
|
[JsonPropertyName("name")] public required string Name { get; init; }
|
||||||
|
[JsonPropertyName("picture")] public required FacebookPictureData Picture { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FacebookPictureData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("data")] public required FacebookPicture Picture { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FacebookPicture
|
||||||
|
{
|
||||||
|
[JsonPropertyName("url")] public required string Url { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[PublicAPI]
|
||||||
|
public record LoginWithFacebookRequest(
|
||||||
|
string Token);
|
||||||
|
|
||||||
|
[PublicAPI]
|
||||||
|
public record LoginWithFacebookResponse(
|
||||||
|
string AccessToken,
|
||||||
|
string RefreshToken);
|
||||||
|
|
||||||
|
[PublicAPI]
|
||||||
|
public class LoginWithFacebookHandler(
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IdentityUserManager userManager,
|
||||||
|
SignInManager<IdentityUser> signInManager,
|
||||||
|
IOptionsSnapshot<JwtOptions> jwtOptions)
|
||||||
|
: Endpoint<LoginWithFacebookRequest, LoginWithFacebookResponse>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
AllowAnonymous();
|
||||||
|
Post("/api/users/login-with-facebook");
|
||||||
|
Options(o => o.WithTags("Users"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(
|
||||||
|
LoginWithFacebookRequest request,
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
// Verify the token with Facebook
|
||||||
|
using var httpClient = httpClientFactory.CreateClient();
|
||||||
|
using var response = await httpClient.GetAsync(
|
||||||
|
$"https://graph.facebook.com/me?access_token={request.Token}&fields=id,name,email,picture.width(200).height(200)",
|
||||||
|
ct);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
await SendStringAsync(
|
||||||
|
"The token is not valid",
|
||||||
|
400,
|
||||||
|
cancellation: ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the user info (email, name, profile picture)
|
||||||
|
var content = await response.Content.ReadAsStringAsync(ct);
|
||||||
|
var userInfo = JsonSerializer.Deserialize<FacebookUserInfo>(content);
|
||||||
|
if (userInfo is null || string.IsNullOrEmpty(userInfo.Id))
|
||||||
|
{
|
||||||
|
await SendStringAsync(
|
||||||
|
"Failed to retrieve user information from Facebook",
|
||||||
|
400,
|
||||||
|
cancellation: ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user exists or create a new one
|
||||||
|
var user = await userManager.FindByEmailAsync(userInfo.Email!);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
var generatedPassword = PasswordGenerator.GeneratePassword(10, 12);
|
||||||
|
var generatedUser = new IdentityUser
|
||||||
|
{
|
||||||
|
UserName = userInfo.Email ?? $"fb_{userInfo.Id}",
|
||||||
|
Email = userInfo.Email,
|
||||||
|
Firstname = userInfo.Name.Split(' ').FirstOrDefault() ?? "",
|
||||||
|
Lastname = userInfo.Name.Split(' ').Skip(1).FirstOrDefault() ?? "",
|
||||||
|
Alias = userInfo.Name,
|
||||||
|
PortraitUrl = userInfo.Picture.Picture.Url,
|
||||||
|
FacebookId = userInfo.Id, // Storing Facebook ID
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await userManager.CreateAsync(
|
||||||
|
generatedUser,
|
||||||
|
generatedPassword);
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
await SendStringAsync(
|
||||||
|
result.Errors.First().Description,
|
||||||
|
400,
|
||||||
|
cancellation: ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = generatedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
await signInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
|
||||||
|
var accessToken = JwtTokenHelper.GenerateJwtToken(
|
||||||
|
expiresIn: jwtOptions.Value.Lifetime,
|
||||||
|
issuer: jwtOptions.Value.Issuer,
|
||||||
|
audience: jwtOptions.Value.Audience,
|
||||||
|
key: jwtOptions.Value.Key,
|
||||||
|
userId: user.Id.ToString(),
|
||||||
|
email: user.Email,
|
||||||
|
alias: user.Alias,
|
||||||
|
firstname: user.Firstname,
|
||||||
|
lastname: user.Lastname,
|
||||||
|
portraitUrl: user.PortraitUrl);
|
||||||
|
|
||||||
|
await SendOkAsync(
|
||||||
|
new LoginWithFacebookResponse(accessToken, string.Empty),
|
||||||
|
cancellation: ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,14 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
namespace Hutopy.Web.Features.Users;
|
namespace Hutopy.Web.Features.Users;
|
||||||
|
|
||||||
public class IdentityUser : IdentityUser<Guid>
|
public class IdentityUser : IdentityUser<Guid>
|
||||||
{
|
{
|
||||||
[MaxLength(255)] public string? Alias { get; set; }
|
[MaxLength(255)] public string? Alias { get; set; }
|
||||||
[MaxLength(255)] public string? Firstname { get; set; }
|
[MaxLength(255)] public string? Firstname { get; set; }
|
||||||
[MaxLength(255)] public string? Lastname { get; set; }
|
[MaxLength(255)] public string? Lastname { get; set; }
|
||||||
public DateTime? BirthDate { get; set; }
|
public DateTime? BirthDate { get; set; }
|
||||||
[MaxLength(255)] public string? Address { get; set; }
|
[MaxLength(255)] public string? Address { get; set; }
|
||||||
[MaxLength(255)] public string? PortraitUrl { get; set; }
|
[MaxLength(255)] public string? PortraitUrl { get; set; }
|
||||||
[MaxLength(255)] public string? GoogleId { get; set; }
|
[MaxLength(255)] public string? GoogleId { get; set; }
|
||||||
|
[MaxLength(255)] public string? FacebookId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
340
frontend/package-lock.json
generated
340
frontend/package-lock.json
generated
@@ -12,6 +12,7 @@
|
|||||||
"@stripe/stripe-js": "^3.0.10",
|
"@stripe/stripe-js": "^3.0.10",
|
||||||
"@tinymce/tinymce-vue": "^6.0.1",
|
"@tinymce/tinymce-vue": "^6.0.1",
|
||||||
"@vueuse/core": "^10.11.0",
|
"@vueuse/core": "^10.11.0",
|
||||||
|
"@vueuse/head": "^2.0.0",
|
||||||
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"i18n": "^0.15.1",
|
"i18n": "^0.15.1",
|
||||||
@@ -1014,16 +1015,6 @@
|
|||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fortawesome/fontawesome-free": {
|
|
||||||
"version": "6.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz",
|
|
||||||
"integrity": "sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@hapi/hoek": {
|
"node_modules/@hapi/hoek": {
|
||||||
"version": "9.3.0",
|
"version": "9.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||||
@@ -1611,16 +1602,6 @@
|
|||||||
"vue": "^3.0.0"
|
"vue": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@trysound/sax": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/configstore": {
|
"node_modules/@types/configstore": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/configstore/-/configstore-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/configstore/-/configstore-2.1.1.tgz",
|
||||||
@@ -1730,6 +1711,76 @@
|
|||||||
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
|
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@unhead/dom": {
|
||||||
|
"version": "1.11.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.11.19.tgz",
|
||||||
|
"integrity": "sha512-udkgITdIblEWH3hsoFQMKW+6QXNO2qFZlZ2FI37bVAplQSnK/PytTPt/5oA1GWkoVwT0DsQNGHbU6kOg/3SlNg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/schema": "1.11.19",
|
||||||
|
"@unhead/shared": "1.11.19"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@unhead/schema": {
|
||||||
|
"version": "1.11.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.11.19.tgz",
|
||||||
|
"integrity": "sha512-7VhYHWK7xHgljdv+C01MepCSYZO2v6OhgsfKWPxRQBDDGfUKCUaChox0XMq3tFvXP6u4zSp6yzcDw2yxCfVMwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"hookable": "^5.5.3",
|
||||||
|
"zhead": "^2.2.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@unhead/shared": {
|
||||||
|
"version": "1.11.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.11.19.tgz",
|
||||||
|
"integrity": "sha512-UYE9EIeQLJOhx8vC71bWGkAGY4Zzq/H8qYlihowUg4NiFOfL+KKMnj96datb74PRxSDvHac9V3OLktNcsX2NuA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/schema": "1.11.19",
|
||||||
|
"packrup": "^0.1.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@unhead/ssr": {
|
||||||
|
"version": "1.11.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/ssr/-/ssr-1.11.19.tgz",
|
||||||
|
"integrity": "sha512-OH+rj6xBTdYyLsSntk4lEQyR+z57aEUZIiR2UpPl1zWGtBZPIr5zs3GY5+EyJ8t8e0zLemPR/Pu7VembTJ8o1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/schema": "1.11.19",
|
||||||
|
"@unhead/shared": "1.11.19"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@unhead/vue": {
|
||||||
|
"version": "1.11.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.11.19.tgz",
|
||||||
|
"integrity": "sha512-/XATTP8wVLs3+2Pkj2crvr/Z55nybVQyOwISh+sAlr/48/9n3jGNiCZHKpHgL4MpOnGT4krwzWzbfhBO/G2BSQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/schema": "1.11.19",
|
||||||
|
"@unhead/shared": "1.11.19",
|
||||||
|
"hookable": "^5.5.3",
|
||||||
|
"unhead": "1.11.19"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">=2.7 || >=3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vee-validate/rules": {
|
"node_modules/@vee-validate/rules": {
|
||||||
"version": "4.13.2",
|
"version": "4.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@vee-validate/rules/-/rules-4.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vee-validate/rules/-/rules-4.13.2.tgz",
|
||||||
@@ -1886,6 +1937,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vueuse/head": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/head/-/head-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ykdOxTGs95xjD4WXE4na/umxZea2Itl0GWBILas+O4oqS7eXIods38INvk3XkJKjqMdWPcpCyLX/DioLQxU1KA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/dom": "^1.7.0",
|
||||||
|
"@unhead/schema": "^1.7.0",
|
||||||
|
"@unhead/ssr": "^1.7.0",
|
||||||
|
"@unhead/vue": "^1.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">=2.7 || >=3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vueuse/metadata": {
|
"node_modules/@vueuse/metadata": {
|
||||||
"version": "10.11.1",
|
"version": "10.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
|
||||||
@@ -2935,50 +3001,6 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/css-select": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"boolbase": "^1.0.0",
|
|
||||||
"css-what": "^6.1.0",
|
|
||||||
"domhandler": "^5.0.2",
|
|
||||||
"domutils": "^3.0.1",
|
|
||||||
"nth-check": "^2.0.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/fb55"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/css-tree": {
|
|
||||||
"version": "2.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
|
||||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mdn-data": "2.0.30",
|
|
||||||
"source-map-js": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/css-what": {
|
|
||||||
"version": "6.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
|
||||||
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/fb55"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cssesc": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
@@ -2990,42 +3012,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/csso": {
|
|
||||||
"version": "5.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
|
||||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"css-tree": "~2.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/csso/node_modules/css-tree": {
|
|
||||||
"version": "2.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
|
||||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mdn-data": "2.0.28",
|
|
||||||
"source-map-js": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/csso/node_modules/mdn-data": {
|
|
||||||
"version": "2.0.28",
|
|
||||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
|
||||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "CC0-1.0"
|
|
||||||
},
|
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
@@ -3306,65 +3292,6 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dom-serializer": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.2",
|
|
||||||
"entities": "^4.2.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/domelementtype": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/fb55"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "BSD-2-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/domhandler": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/domutils": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"dom-serializer": "^2.0.0",
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dot-prop": {
|
"node_modules/dot-prop": {
|
||||||
"version": "9.0.0",
|
"version": "9.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
|
||||||
@@ -4464,6 +4391,12 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hookable": {
|
||||||
|
"version": "5.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||||
|
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/http-errors": {
|
"node_modules/http-errors": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
@@ -5302,13 +5235,6 @@
|
|||||||
"is-buffer": "~1.1.6"
|
"is-buffer": "~1.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mdn-data": {
|
|
||||||
"version": "2.0.30",
|
|
||||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
|
||||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "CC0-1.0"
|
|
||||||
},
|
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
@@ -5864,6 +5790,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
||||||
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/packrup": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/packrup/-/packrup-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-ZcKU7zrr5GlonoS9cxxrb5HVswGnyj6jQvwFBa6p5VFw7G71VAHcUKL5wyZSU/ECtPM/9gacWxy2KFQKt1gMNA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@@ -7143,42 +7078,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svgo": {
|
|
||||||
"version": "3.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
|
|
||||||
"integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@trysound/sax": "0.2.0",
|
|
||||||
"commander": "^7.2.0",
|
|
||||||
"css-select": "^5.1.0",
|
|
||||||
"css-tree": "^2.3.1",
|
|
||||||
"css-what": "^6.1.0",
|
|
||||||
"csso": "^5.0.5",
|
|
||||||
"picocolors": "^1.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"svgo": "bin/svgo"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/svgo"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svgo/node_modules/commander": {
|
|
||||||
"version": "7.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
|
||||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.10",
|
"version": "3.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz",
|
||||||
@@ -7393,6 +7292,21 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/unhead": {
|
||||||
|
"version": "1.11.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/unhead/-/unhead-1.11.19.tgz",
|
||||||
|
"integrity": "sha512-O5AYb3+xUOzBlwDmPfC/DgGp9rDMoGkB4gFkhoaz8IonQqP8W8qqetxYf5ZyEdntvXnFsMWS8lZF//5176xo6Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/dom": "1.11.19",
|
||||||
|
"@unhead/schema": "1.11.19",
|
||||||
|
"@unhead/shared": "1.11.19",
|
||||||
|
"hookable": "^5.5.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
@@ -7590,19 +7504,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-svg-loader": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"svgo": "^3.0.2"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": ">=3.2.13"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vue": {
|
"node_modules/vue": {
|
||||||
"version": "3.4.38",
|
"version": "3.4.38",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz",
|
||||||
@@ -8085,6 +7986,15 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zhead": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"@stripe/stripe-js": "^3.0.10",
|
"@stripe/stripe-js": "^3.0.10",
|
||||||
"@tinymce/tinymce-vue": "^6.0.1",
|
"@tinymce/tinymce-vue": "^6.0.1",
|
||||||
"@vueuse/core": "^10.11.0",
|
"@vueuse/core": "^10.11.0",
|
||||||
|
"@vueuse/head": "^2.0.0",
|
||||||
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"i18n": "^0.15.1",
|
"i18n": "^0.15.1",
|
||||||
|
|||||||
76
frontend/src/composables/useFacebookLogin.js
Normal file
76
frontend/src/composables/useFacebookLogin.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import {useHead} from "@vueuse/head";
|
||||||
|
import {useAuthStore} from "@/stores/authStore.js";
|
||||||
|
|
||||||
|
export function useFacebookLogin() {
|
||||||
|
const isSdkLoaded = ref(false);
|
||||||
|
|
||||||
|
/* TODO: FIND THE ACTUAL HUTOPY'S APP_ID */
|
||||||
|
const FACEBOOK_APP_ID = "1076433907621883";
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
script: [
|
||||||
|
{
|
||||||
|
src: "https://connect.facebook.net/en_US/sdk.js",
|
||||||
|
async: true,
|
||||||
|
defer: true,
|
||||||
|
onload: "initializeFacebookSDK()",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
(function (d, s, id) {
|
||||||
|
var js, fjs = d.getElementsByTagName(s)[0];
|
||||||
|
if (d.getElementById(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
js = d.createElement(s);
|
||||||
|
js.id = id;
|
||||||
|
js.src = "https://connect.facebook.net/en_US/sdk.js";
|
||||||
|
fjs.parentNode.insertBefore(js, fjs);
|
||||||
|
}(document, 'script', 'facebook-jssdk'));
|
||||||
|
|
||||||
|
const initializeFacebookSDK = () => {
|
||||||
|
window.fbAsyncInit = function () {
|
||||||
|
FB.init({
|
||||||
|
appId: FACEBOOK_APP_ID,
|
||||||
|
xfbml: true,
|
||||||
|
version: 'v22.0'
|
||||||
|
});
|
||||||
|
FB.AppEvents.logPageView();
|
||||||
|
|
||||||
|
isSdkLoaded.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginWithFacebook = () => {
|
||||||
|
if (!isSdkLoaded.value) {
|
||||||
|
console.error("Facebook SDK non encore chargé !");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.FB.login(
|
||||||
|
(response) => {
|
||||||
|
if (response.authResponse) {
|
||||||
|
console.log("Utilisateur connecté :", response);
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
authStore.loginWithFacebook(response.authResponse);
|
||||||
|
} else {
|
||||||
|
console.log("Connexion annulée ou échouée.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scope: "public_profile,email"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initializeFacebookSDK();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
loginWithFacebook,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,39 +1,13 @@
|
|||||||
<template>
|
|
||||||
|
|
||||||
<div class="flex min-h-screen items-center">
|
|
||||||
<div class="card justify-items-center">
|
|
||||||
|
|
||||||
<img alt="hutopy login"
|
|
||||||
src="/images/hutopy-logo.png"/>
|
|
||||||
|
|
||||||
<div class="flex flex-col w-[800px] gap-10 justify-items-center">
|
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold login-text text-center mt-10">
|
|
||||||
Connexion
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<google-login :callback="googleCallback"
|
|
||||||
class="w-full justify-items-center"
|
|
||||||
popup-type="TOKEN">
|
|
||||||
<button class="secondary w-full">
|
|
||||||
<v-icon left>mdi-google</v-icon>
|
|
||||||
Google
|
|
||||||
</button>
|
|
||||||
</google-login>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref} from 'vue';
|
import {ref} from 'vue';
|
||||||
import {GoogleLogin} from "vue3-google-login";
|
import {GoogleLogin} from "vue3-google-login";
|
||||||
import {useAuthStore} from '@/stores/authStore.js';
|
import {useAuthStore} from '@/stores/authStore.js';
|
||||||
|
|
||||||
|
import {useFacebookLogin} from "@/composables/useFacebookLogin";
|
||||||
|
import Facebook from "@/views/svg/Facebook.vue";
|
||||||
|
|
||||||
|
const {loginWithFacebook} = useFacebookLogin();
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const errorSnackBar = ref(false);
|
const errorSnackBar = ref(false);
|
||||||
@@ -46,3 +20,49 @@ async function googleCallback(token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="flex min-h-screen items-center">
|
||||||
|
<div class="card justify-items-center">
|
||||||
|
|
||||||
|
<img alt="hutopy login"
|
||||||
|
src="/images/hutopy-logo.png"/>
|
||||||
|
|
||||||
|
<div class="flex flex-col w-[800px] gap-10 justify-items-center">
|
||||||
|
|
||||||
|
<h1 class="text-2xl font-bold login-text text-center mt-10 ">
|
||||||
|
Connexion
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="w-full items-center justify-center flex flex-col gap-4">
|
||||||
|
<google-login :callback="googleCallback"
|
||||||
|
class=""
|
||||||
|
popup-type="TOKEN">
|
||||||
|
<button class="secondary w-full">
|
||||||
|
<v-icon left>mdi-google</v-icon>
|
||||||
|
Google
|
||||||
|
</button>
|
||||||
|
</google-login>
|
||||||
|
|
||||||
|
<button class="secondary w-full text-red-600"
|
||||||
|
@click="loginWithFacebook">
|
||||||
|
<facebook class="social-icon"></facebook>
|
||||||
|
Facebook
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.social-icon {
|
||||||
|
@apply w-5 h-5;
|
||||||
|
@apply text-base;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user