From a695e1cce6122dadcdeec10744f98d43a6533dc8 Mon Sep 17 00:00:00 2001 From: lumijiez <59575049+lumijiez@users.noreply.github.com> Date: Mon, 9 Jun 2025 00:45:42 +0300 Subject: [PATCH] Jostka --- src/Imprink.Application/IUnitOfWork.cs | 1 + .../Users/SyncUserHandler.cs | 31 ++ src/Imprink.Domain/Common/Models/Auth0User.cs | 2 - src/Imprink.Domain/Entities/Users/User.cs | 12 +- .../Repositories/IUserRepository.cs | 13 +- .../Configuration/Users/UserConfiguration.cs | 22 +- ...> 20250608204903_InitialSetup.Designer.cs} | 20 +- ...etup.cs => 20250608204903_InitialSetup.cs} | 10 +- .../ApplicationDbContextModelSnapshot.cs | 18 +- .../Repositories/UserRepository.cs | 100 ++----- src/Imprink.Infrastructure/UnitOfWork.cs | 5 +- .../Controllers/Users/UserController.cs | 30 +- src/Imprink.WebApi/Startup.cs | 5 +- webui/package-lock.json | 280 ++++++++++++++++++ webui/package.json | 1 + webui/src/app/page.js | 29 +- webui/src/app/token/route.js | 28 +- webui/src/app/untoken/route.js | 22 ++ webui/src/lib/api.js | 8 + 19 files changed, 464 insertions(+), 173 deletions(-) create mode 100644 src/Imprink.Application/Users/SyncUserHandler.cs rename src/Imprink.Infrastructure/Migrations/{20250607211109_InitialSetup.Designer.cs => 20250608204903_InitialSetup.Designer.cs} (98%) rename src/Imprink.Infrastructure/Migrations/{20250607211109_InitialSetup.cs => 20250608204903_InitialSetup.cs} (98%) create mode 100644 webui/src/app/untoken/route.js create mode 100644 webui/src/lib/api.js diff --git a/src/Imprink.Application/IUnitOfWork.cs b/src/Imprink.Application/IUnitOfWork.cs index fbc2f86..f76067f 100644 --- a/src/Imprink.Application/IUnitOfWork.cs +++ b/src/Imprink.Application/IUnitOfWork.cs @@ -7,6 +7,7 @@ public interface IUnitOfWork public IProductRepository ProductRepository { get; } public ICategoryRepository CategoryRepository { get; } public IProductVariantRepository ProductVariantRepository { get; } + public IUserRepository UserRepository { get; } Task SaveAsync(CancellationToken cancellationToken = default); Task BeginTransactionAsync(CancellationToken cancellationToken = default); diff --git a/src/Imprink.Application/Users/SyncUserHandler.cs b/src/Imprink.Application/Users/SyncUserHandler.cs new file mode 100644 index 0000000..2a73cae --- /dev/null +++ b/src/Imprink.Application/Users/SyncUserHandler.cs @@ -0,0 +1,31 @@ +using Imprink.Domain.Common.Models; +using MediatR; + +namespace Imprink.Application.Users; + +public record SyncUserCommand(Auth0User User) : IRequest; + +public class SyncUserHandler(IUnitOfWork uw): IRequestHandler +{ + public async Task Handle(SyncUserCommand request, CancellationToken cancellationToken) + { + await uw.BeginTransactionAsync(cancellationToken); + + try + { + if (!await uw.UserRepository.UpdateOrCreateUserAsync(request.User, cancellationToken)) + { + await uw.RollbackTransactionAsync(cancellationToken); + } + + await uw.SaveAsync(cancellationToken); + await uw.CommitTransactionAsync(cancellationToken); + return true; + } + catch + { + await uw.RollbackTransactionAsync(cancellationToken); + throw; + } + } +} \ No newline at end of file diff --git a/src/Imprink.Domain/Common/Models/Auth0User.cs b/src/Imprink.Domain/Common/Models/Auth0User.cs index 5c8b7a1..687980e 100644 --- a/src/Imprink.Domain/Common/Models/Auth0User.cs +++ b/src/Imprink.Domain/Common/Models/Auth0User.cs @@ -1,5 +1,3 @@ -using System.ComponentModel.DataAnnotations; - namespace Imprink.Domain.Common.Models; public class Auth0User diff --git a/src/Imprink.Domain/Entities/Users/User.cs b/src/Imprink.Domain/Entities/Users/User.cs index 8f81a99..19d8ed1 100644 --- a/src/Imprink.Domain/Entities/Users/User.cs +++ b/src/Imprink.Domain/Entities/Users/User.cs @@ -5,19 +5,19 @@ namespace Imprink.Domain.Entities.Users; public class User { public string Id { get; set; } = null!; + public required string Name { get; set; } + public required string Nickname { get; set; } public required string Email { get; set; } - public required string FirstName { get; set; } - public required string LastName { get; set; } + + public bool EmailVerified { get; set; } + public string? FullName { get; set; } public string? PhoneNumber { get; set; } - public DateTime? DateOfBirth { get; set; } public required bool IsActive { get; set; } - public DateTime? LastLoginAt { get; set; } public virtual ICollection
Addresses { get; set; } = new List
(); public virtual ICollection UserRoles { get; set; } = new List(); public virtual ICollection Orders { get; set; } = new List(); - public string FullName => $"{FirstName} {LastName}"; - public Address? DefaultAddress => Addresses.FirstOrDefault(a => a.IsDefault && a.IsActive); + public Address? DefaultAddress => Addresses.FirstOrDefault(a => a is { IsDefault: true, IsActive: true }); public IEnumerable Roles => UserRoles.Select(ur => ur.Role); } \ No newline at end of file diff --git a/src/Imprink.Domain/Repositories/IUserRepository.cs b/src/Imprink.Domain/Repositories/IUserRepository.cs index 145583d..59b6f5d 100644 --- a/src/Imprink.Domain/Repositories/IUserRepository.cs +++ b/src/Imprink.Domain/Repositories/IUserRepository.cs @@ -1,9 +1,11 @@ +using Imprink.Domain.Common.Models; using Imprink.Domain.Entities.Users; namespace Imprink.Domain.Repositories; public interface IUserRepository { + Task UpdateOrCreateUserAsync(Auth0User user, CancellationToken cancellationToken = default); Task GetUserByIdAsync(string userId, CancellationToken cancellationToken = default); Task GetUserByEmailAsync(string email, CancellationToken cancellationToken = default); Task> GetAllUsersAsync(CancellationToken cancellationToken = default); @@ -18,19 +20,8 @@ public interface IUserRepository Task ActivateUserAsync(string userId, CancellationToken cancellationToken = default); Task DeactivateUserAsync(string userId, CancellationToken cancellationToken = default); - Task UpdateLastLoginAsync(string userId, DateTime loginTime, CancellationToken cancellationToken = default); - Task> SearchUsersAsync(string searchTerm, CancellationToken cancellationToken = default); Task> GetUsersByRoleAsync(Guid roleId, CancellationToken cancellationToken = default); - Task<(IEnumerable Users, int TotalCount)> GetUsersPagedAsync( - int pageNumber, - int pageSize, - string? searchTerm = null, - bool? isActive = null, - CancellationToken cancellationToken = default); - - Task GetUserWithAddressesAsync(string userId, CancellationToken cancellationToken = default); - Task GetUserWithRolesAsync(string userId, CancellationToken cancellationToken = default); Task GetUserWithAllRelatedDataAsync(string userId, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Imprink.Infrastructure/Configuration/Users/UserConfiguration.cs b/src/Imprink.Infrastructure/Configuration/Users/UserConfiguration.cs index 31c74a7..8003def 100644 --- a/src/Imprink.Infrastructure/Configuration/Users/UserConfiguration.cs +++ b/src/Imprink.Infrastructure/Configuration/Users/UserConfiguration.cs @@ -15,28 +15,29 @@ public class UserConfiguration : IEntityTypeConfiguration builder.Property(u => u.Email) .IsRequired() .HasMaxLength(256); - - builder.Property(u => u.FirstName) + + builder.Property(u => u.Name) .IsRequired() .HasMaxLength(100); - - builder.Property(u => u.LastName) + + builder.Property(u => u.Nickname) .IsRequired() .HasMaxLength(100); + + builder.Property(u => u.EmailVerified) + .IsRequired() + .HasMaxLength(100); + + builder.Property(u => u.FullName) + .HasMaxLength(100); builder.Property(u => u.PhoneNumber) .HasMaxLength(20); - builder.Property(u => u.DateOfBirth) - .HasColumnType("date"); - builder.Property(u => u.IsActive) .IsRequired() .HasDefaultValue(true); - builder.Property(u => u.LastLoginAt) - .HasColumnType("datetime2"); - builder.HasIndex(u => u.Email) .IsUnique() .HasDatabaseName("IX_User_Email"); @@ -50,7 +51,6 @@ public class UserConfiguration : IEntityTypeConfiguration .HasPrincipalKey(u => u.Id) .OnDelete(DeleteBehavior.Cascade); - builder.Ignore(u => u.FullName); builder.Ignore(u => u.DefaultAddress); builder.Ignore(u => u.Roles); } diff --git a/src/Imprink.Infrastructure/Migrations/20250607211109_InitialSetup.Designer.cs b/src/Imprink.Infrastructure/Migrations/20250608204903_InitialSetup.Designer.cs similarity index 98% rename from src/Imprink.Infrastructure/Migrations/20250607211109_InitialSetup.Designer.cs rename to src/Imprink.Infrastructure/Migrations/20250608204903_InitialSetup.Designer.cs index dd22c86..c07716e 100644 --- a/src/Imprink.Infrastructure/Migrations/20250607211109_InitialSetup.Designer.cs +++ b/src/Imprink.Infrastructure/Migrations/20250608204903_InitialSetup.Designer.cs @@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Imprink.Infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20250607211109_InitialSetup")] + [Migration("20250608204903_InitialSetup")] partial class InitialSetup { /// @@ -766,16 +766,16 @@ namespace Imprink.Infrastructure.Migrations .HasMaxLength(450) .HasColumnType("nvarchar(450)"); - b.Property("DateOfBirth") - .HasColumnType("date"); - b.Property("Email") .IsRequired() .HasMaxLength(256) .HasColumnType("nvarchar(256)"); - b.Property("FirstName") - .IsRequired() + b.Property("EmailVerified") + .HasMaxLength(100) + .HasColumnType("bit"); + + b.Property("FullName") .HasMaxLength(100) .HasColumnType("nvarchar(100)"); @@ -784,10 +784,12 @@ namespace Imprink.Infrastructure.Migrations .HasColumnType("bit") .HasDefaultValue(true); - b.Property("LastLoginAt") - .HasColumnType("datetime2"); + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); - b.Property("LastName") + b.Property("Nickname") .IsRequired() .HasMaxLength(100) .HasColumnType("nvarchar(100)"); diff --git a/src/Imprink.Infrastructure/Migrations/20250607211109_InitialSetup.cs b/src/Imprink.Infrastructure/Migrations/20250608204903_InitialSetup.cs similarity index 98% rename from src/Imprink.Infrastructure/Migrations/20250607211109_InitialSetup.cs rename to src/Imprink.Infrastructure/Migrations/20250608204903_InitialSetup.cs index 0fee439..f666300 100644 --- a/src/Imprink.Infrastructure/Migrations/20250607211109_InitialSetup.cs +++ b/src/Imprink.Infrastructure/Migrations/20250608204903_InitialSetup.cs @@ -81,13 +81,13 @@ namespace Imprink.Infrastructure.Migrations columns: table => new { Id = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Nickname = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - FirstName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), - LastName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + EmailVerified = table.Column(type: "bit", maxLength: 100, nullable: false), + FullName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), PhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true), - DateOfBirth = table.Column(type: "date", nullable: true), - IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), - LastLoginAt = table.Column(type: "datetime2", nullable: true) + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true) }, constraints: table => { diff --git a/src/Imprink.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Imprink.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 2741704..ee202dc 100644 --- a/src/Imprink.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Imprink.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -763,16 +763,16 @@ namespace Imprink.Infrastructure.Migrations .HasMaxLength(450) .HasColumnType("nvarchar(450)"); - b.Property("DateOfBirth") - .HasColumnType("date"); - b.Property("Email") .IsRequired() .HasMaxLength(256) .HasColumnType("nvarchar(256)"); - b.Property("FirstName") - .IsRequired() + b.Property("EmailVerified") + .HasMaxLength(100) + .HasColumnType("bit"); + + b.Property("FullName") .HasMaxLength(100) .HasColumnType("nvarchar(100)"); @@ -781,10 +781,12 @@ namespace Imprink.Infrastructure.Migrations .HasColumnType("bit") .HasDefaultValue(true); - b.Property("LastLoginAt") - .HasColumnType("datetime2"); + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); - b.Property("LastName") + b.Property("Nickname") .IsRequired() .HasMaxLength(100) .HasColumnType("nvarchar(100)"); diff --git a/src/Imprink.Infrastructure/Repositories/UserRepository.cs b/src/Imprink.Infrastructure/Repositories/UserRepository.cs index 7addee9..fb6e1d0 100644 --- a/src/Imprink.Infrastructure/Repositories/UserRepository.cs +++ b/src/Imprink.Infrastructure/Repositories/UserRepository.cs @@ -1,3 +1,4 @@ +using Imprink.Domain.Common.Models; using Imprink.Domain.Entities.Users; using Imprink.Domain.Repositories; using Imprink.Infrastructure.Database; @@ -7,6 +8,37 @@ namespace Imprink.Infrastructure.Repositories; public class UserRepository(ApplicationDbContext context) : IUserRepository { + public async Task UpdateOrCreateUserAsync(Auth0User user, CancellationToken cancellationToken = default) + { + var userToUpdate = await context.Users + .Where(u => u.Id.Equals(user.Sub)) + .FirstOrDefaultAsync(cancellationToken); + + if (userToUpdate == null) + { + var newUser = new User + { + Id = user.Sub, + Email = user.Email, + EmailVerified = user.EmailVerified, + Name = user.Name, + Nickname = user.Nickname, + IsActive = true + }; + + context.Users.Add(newUser); + } + else + { + userToUpdate.Email = user.Email; + userToUpdate.Name = user.Name; + userToUpdate.Nickname = user.Nickname; + userToUpdate.EmailVerified = user.EmailVerified; + } + + return true; + } + public async Task GetUserByIdAsync(string userId, CancellationToken cancellationToken = default) { return await context.Users @@ -92,26 +124,6 @@ public class UserRepository(ApplicationDbContext context) : IUserRepository return true; } - public async Task UpdateLastLoginAsync(string userId, DateTime loginTime, CancellationToken cancellationToken = default) - { - var user = await context.Users.FindAsync(new object[] { userId }, cancellationToken); - if (user != null) - { - user.LastLoginAt = loginTime; - await context.SaveChangesAsync(cancellationToken); - } - } - - public async Task> SearchUsersAsync(string searchTerm, CancellationToken cancellationToken = default) - { - return await context.Users - .AsNoTracking() - .Where(u => u.Email.Contains(searchTerm) || - u.FirstName.Contains(searchTerm) || - u.LastName.Contains(searchTerm)) - .ToListAsync(cancellationToken); - } - public async Task> GetUsersByRoleAsync(Guid roleId, CancellationToken cancellationToken = default) { return await context.Users @@ -120,54 +132,6 @@ public class UserRepository(ApplicationDbContext context) : IUserRepository .ToListAsync(cancellationToken); } - public async Task<(IEnumerable Users, int TotalCount)> GetUsersPagedAsync( - int pageNumber, - int pageSize, - string? searchTerm = null, - bool? isActive = null, - CancellationToken cancellationToken = default) - { - var query = context.Users.AsNoTracking(); - - if (!string.IsNullOrEmpty(searchTerm)) - { - query = query.Where(u => u.Email.Contains(searchTerm) || - u.FirstName.Contains(searchTerm) || - u.LastName.Contains(searchTerm)); - } - - if (isActive.HasValue) - { - query = query.Where(u => u.IsActive == isActive.Value); - } - - var totalCount = await query.CountAsync(cancellationToken); - - var users = await query - .Skip((pageNumber - 1) * pageSize) - .Take(pageSize) - .ToListAsync(cancellationToken); - - return (users, totalCount); - } - - public async Task GetUserWithAddressesAsync(string userId, CancellationToken cancellationToken = default) - { - return await context.Users - .AsNoTracking() - .Include(u => u.Addresses) - .FirstOrDefaultAsync(u => u.Id == userId, cancellationToken); - } - - public async Task GetUserWithRolesAsync(string userId, CancellationToken cancellationToken = default) - { - return await context.Users - .AsNoTracking() - .Include(u => u.UserRoles) - .ThenInclude(ur => ur.Role) - .FirstOrDefaultAsync(u => u.Id == userId, cancellationToken); - } - public async Task GetUserWithAllRelatedDataAsync(string userId, CancellationToken cancellationToken = default) { return await context.Users diff --git a/src/Imprink.Infrastructure/UnitOfWork.cs b/src/Imprink.Infrastructure/UnitOfWork.cs index 3386e7c..317cdd8 100644 --- a/src/Imprink.Infrastructure/UnitOfWork.cs +++ b/src/Imprink.Infrastructure/UnitOfWork.cs @@ -1,6 +1,7 @@ using Imprink.Application; using Imprink.Domain.Repositories; using Imprink.Infrastructure.Database; +using Microsoft.Identity.Client; namespace Imprink.Infrastructure; @@ -8,11 +9,13 @@ public class UnitOfWork( ApplicationDbContext context, IProductRepository productRepository, IProductVariantRepository productVariantRepository, - ICategoryRepository categoryRepository) : IUnitOfWork + ICategoryRepository categoryRepository, + IUserRepository userRepository) : IUnitOfWork { public IProductRepository ProductRepository => productRepository; public IProductVariantRepository ProductVariantRepository => productVariantRepository; public ICategoryRepository CategoryRepository => categoryRepository; + public IUserRepository UserRepository => userRepository; public async Task SaveAsync(CancellationToken cancellationToken = default) { diff --git a/src/Imprink.WebApi/Controllers/Users/UserController.cs b/src/Imprink.WebApi/Controllers/Users/UserController.cs index ee104b8..89615b0 100644 --- a/src/Imprink.WebApi/Controllers/Users/UserController.cs +++ b/src/Imprink.WebApi/Controllers/Users/UserController.cs @@ -1,23 +1,33 @@ +using System.Security.Claims; +using Imprink.Application.Users; +using Imprink.Domain.Common.Models; +using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Imprink.WebApi.Controllers.Users; [ApiController] -[Route("/api/[controller]")] -public class UserController : ControllerBase +[Route("/users")] +public class UserController(IMediator mediator) : ControllerBase { [Authorize] - [HttpPost] - public IActionResult SyncUser() + [HttpPost("sync")] + public async Task Sync() { - var claims = User.Claims; - - foreach (var claim in claims) + var enumerable = User.Claims as Claim[] ?? User.Claims.ToArray(); + + var auth0User = new Auth0User { - Console.WriteLine($"Claim Type: {claim.Type}, Claim Value: {claim.Value}"); - } + Sub = enumerable.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? string.Empty, + Name = enumerable.FirstOrDefault(c => c.Type == "name")?.Value ?? string.Empty, + Nickname = enumerable.FirstOrDefault(c => c.Type == "nickname")?.Value ?? string.Empty, + Email = enumerable.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value ?? string.Empty, + EmailVerified = bool.TryParse(enumerable.FirstOrDefault(c => c.Type == "email_verified")?.Value, out var emailVerified) && emailVerified + }; + + await mediator.Send(new SyncUserCommand(auth0User)); - return Ok("Claims logged to console."); + return Ok("User Synced."); } } \ No newline at end of file diff --git a/src/Imprink.WebApi/Startup.cs b/src/Imprink.WebApi/Startup.cs index b3070bd..5d8d602 100644 --- a/src/Imprink.WebApi/Startup.cs +++ b/src/Imprink.WebApi/Startup.cs @@ -112,9 +112,6 @@ public static class Startup app.UseAuthentication(); app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } \ No newline at end of file diff --git a/webui/package-lock.json b/webui/package-lock.json index 3fc5eb5..0b2e382 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@auth0/nextjs-auth0": "^4.6.1", + "axios": "^1.9.0", "next": "15.3.3", "react": "^19.0.0", "react-dom": "^19.0.0" @@ -978,6 +979,23 @@ "tailwindcss": "4.1.8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -989,6 +1007,19 @@ "node": ">=10.16.0" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001720", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", @@ -1070,6 +1101,27 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1089,6 +1141,20 @@ "node": ">=8" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -1103,6 +1169,145 @@ "node": ">=10.13.0" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1110,6 +1315,45 @@ "dev": true, "license": "ISC" }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -1385,6 +1629,36 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -1568,6 +1842,12 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", diff --git a/webui/package.json b/webui/package.json index 6db78c1..b94a40a 100644 --- a/webui/package.json +++ b/webui/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@auth0/nextjs-auth0": "^4.6.1", + "axios": "^1.9.0", "next": "15.3.3", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/webui/src/app/page.js b/webui/src/app/page.js index e3e8199..028c0cf 100644 --- a/webui/src/app/page.js +++ b/webui/src/app/page.js @@ -5,22 +5,20 @@ import {useEffect, useState} from "react"; export default function Home() { const { user, error, isLoading } = useUser(); - const [accessToken, setAccessToken] = useState(null); useEffect(() => { const fetchAccessToken = async () => { if (user) { try { - const response = await fetch('/auth/access-token'); const v = await fetch('/token'); - if (response.ok) { - const tokenData = await response.text(); - setAccessToken(tokenData); - } else { - setAccessToken('Token not available'); - } } catch (error) { - setAccessToken('Error fetching token'); + console.error("Error fetching token"); + } + } else { + try { + const resp = await fetch('/untoken'); + } catch (e) { + console.error('Error in /api/untoken:', e); } } }; @@ -28,10 +26,6 @@ export default function Home() { fetchAccessToken().then(r => console.log(r)); }, [user]); - async function checkValidity() { - const check = await fetch('https://impr.ink/api/api/User', {method: 'POST'}); - } - if (isLoading) { return (
@@ -147,15 +141,6 @@ export default function Home() {
)} -
- -
- {accessToken} -
-
diff --git a/webui/src/app/token/route.js b/webui/src/app/token/route.js index bfe4df4..399a38d 100644 --- a/webui/src/app/token/route.js +++ b/webui/src/app/token/route.js @@ -1,34 +1,30 @@ -import { cookies } from 'next/headers'; +import {cookies, headers} from 'next/headers'; import { NextResponse } from 'next/server'; import {auth0} from "@/lib/auth0"; +import api from "@/lib/api"; export async function GET() { try { - const session = await auth0.getSession(); - const accessToken = session.tokenSet.accessToken; - if (!accessToken) { - return NextResponse.json({ error: 'No access token found' }, { status: 401 }); - } + const token = (await auth0.getSession()).tokenSet.accessToken; - const response = NextResponse.json({ message: 'Access token set in cookie' }); + if (!token) { return NextResponse.json({ error: 'No access token found' }, { status: 401 }); } - const cookieDomain = process.env.COOKIE_DOMAIN || undefined; - - const cookieStore = await cookies(); - cookieStore.set({ - name: 'access_token', - value: accessToken, + (await cookies()).set('access_token', token, { httpOnly: true, secure: true, sameSite: 'strict', path: '/', - domain: cookieDomain, + domain: process.env.COOKIE_DOMAIN, maxAge: 3600, }); - return response; + await api.post('/users/sync', {}, { + headers: { Cookie: `access_token=${token}` } + }); + + NextResponse.json({ message: 'Access token set in cookie' }); } catch (error) { - console.error('Error in /api/set-token:', error); + console.error('Error in /api/token:', error); return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); } } \ No newline at end of file diff --git a/webui/src/app/untoken/route.js b/webui/src/app/untoken/route.js new file mode 100644 index 0000000..cd053fe --- /dev/null +++ b/webui/src/app/untoken/route.js @@ -0,0 +1,22 @@ +import {cookies} from 'next/headers'; +import {NextResponse} from 'next/server'; + +export async function GET() { + try { + (await cookies()).set({ + name: 'access_token', + value: '', + httpOnly: true, + secure: true, + sameSite: 'strict', + path: '/', + domain: process.env.COOKIE_DOMAIN, + maxAge: -1, + }); + + return NextResponse.json({message: 'Deleted access token'}); + } catch (error) { + console.error('Error in /api/untoken:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/webui/src/lib/api.js b/webui/src/lib/api.js new file mode 100644 index 0000000..0b1cb3c --- /dev/null +++ b/webui/src/lib/api.js @@ -0,0 +1,8 @@ +import axios from "axios"; + +const api = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, + withCredentials: true, +}); + +export default api; \ No newline at end of file