diff --git a/.Net/Payment/Stripe - Phat.Tran/Documents/Stripe_PhatTran.pptx b/.Net/Payment/Stripe - Phat.Tran/Documents/Stripe_PhatTran.pptx new file mode 100644 index 00000000..0c05f5bd Binary files /dev/null and b/.Net/Payment/Stripe - Phat.Tran/Documents/Stripe_PhatTran.pptx differ diff --git a/.Net/Payment/Stripe - Phat.Tran/README.md b/.Net/Payment/Stripe - Phat.Tran/README.md new file mode 100644 index 00000000..10b7fc7c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/README.md @@ -0,0 +1,8 @@ +# Planning-Book +Demo List: +- Create Customer +- Create Payment Method +- Checkout: One time, Subscription (Save payment method to customer for other checkout in future) +- Payment intent +- Discount/ Promotion Code +- Cancel Subscription diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/.editorconfig b/.Net/Payment/Stripe - Phat.Tran/Servers/.editorconfig new file mode 100644 index 00000000..40381d96 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CS8600: Converting null literal or possible null value to non-nullable type. +dotnet_diagnostic.CS8600.severity = none diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/BaseRelationDbEntityTypeConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/BaseRelationDbEntityTypeConfiguration.cs new file mode 100644 index 00000000..ed88ba0b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/BaseRelationDbEntityTypeConfiguration.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System.Reflection; + +namespace PlanningBook.DBEngine +{ + public abstract class BaseRelationDbEntityTypeConfiguration : IBaseEntityTypeConfiguration + where TEntity : class + { + public virtual void Configure(EntityTypeBuilder builder) + { + builder.ToTable($"{typeof(TEntity).Name}s"); + + var hasIdColumn = typeof(TEntity).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance); + if(hasIdColumn != null) + { + builder.HasKey("Id"); + } + } + } +} \ No newline at end of file diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/Constants/DBEngineConstants.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/Constants/DBEngineConstants.cs new file mode 100644 index 00000000..1281117e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/Constants/DBEngineConstants.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.DBEngine.Constants +{ + public static class DBEngineConstants + { + public const string RootConnectionString = "DatabaseConnectionStrings"; + public const string dbConnectionStringPrefix = "ConnectionString"; + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/IBaseEntityTypeConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/IBaseEntityTypeConfiguration.cs new file mode 100644 index 00000000..d2531e50 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/IBaseEntityTypeConfiguration.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace PlanningBook.DBEngine +{ + public interface IBaseEntityTypeConfiguration : IEntityTypeConfiguration + where TEntity : class + { + //void Configure(EntityTypeBuilder builder); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/PlanningBook.DBEngine.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/PlanningBook.DBEngine.csproj new file mode 100644 index 00000000..49d860f6 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/PlanningBook.DBEngine/PlanningBook.DBEngine.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/EFRepository.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/EFRepository.cs new file mode 100644 index 00000000..c3164384 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/EFRepository.cs @@ -0,0 +1,648 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using PlanningBook.Domain; +using PlanningBook.Domain.Constant; +using PlanningBook.Domain.Enums; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF.Extensions; +using System.Linq.Expressions; +using System.Reflection; + +namespace PlanningBook.Repository.EF +{ + public class EFRepository : IEFRepository + where TDbContext : DbContext + where TEntity : EntityBase + { + protected readonly TDbContext _dbContext; + public EFRepository(TDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task AddAsync(TEntity entity, CancellationToken cancellationToken = default) + { + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + + await _dbContext.Set().AddAsync(entity, cancellationToken); + return entity; + } + + public async Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) + { + if (entities.FirstOrDefault() is IDateAudited) + { + foreach (var entity in entities) + { + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + } + } + + await _dbContext.Set().AddRangeAsync(entities, cancellationToken); + return entities; + } + + public async Task CountAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + if (whereCondition == null) + return await _dbContext.Set().CountAsync(cancellationToken); + + return await _dbContext.Set().CountAsync(whereCondition, cancellationToken); + } + + public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) + { + await Task.Yield(); + _dbContext.Set().Remove(entity); + } + + public async Task DeleteRangeAsync(List entities, CancellationToken cancellationToken = default) + { + await Task.Yield(); + _dbContext.Set().RemoveRange(entities); + } + + public async Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + var result = _dbContext.Set().AsNoTracking().AsQueryable(); + var elementType = result.ElementType; + var isDateAuditable = typeof(IDateAudited).IsAssignableFrom(elementType); + + if (whereCondition != null) + result = result.Where(whereCondition); + + if (sortCriteria.Any()) + { + bool isFirstSortQuery = true; + foreach (var criteria in sortCriteria) + { + var sortColumnName = criteria.Item1; + var sortDirection = criteria.Item2; + + if (isFirstSortQuery) + { + isFirstSortQuery = false; + result = sortDirection == SortDirection.Ascending ? + result.OrderBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) : + result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + else + { + result = sortDirection == SortDirection.Ascending + ? ((IOrderedQueryable)result).ThenBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) + : ((IOrderedQueryable)result).ThenByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + } + } + else + { + PropertyInfo? propertyInfo = result.GetType().GetGenericArguments()[0].GetProperty(SortColumnConstants.DefaultSortColumn); + if (propertyInfo != null) + result = result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, SortColumnConstants.DefaultSortColumn)); + } + + return await result.AsNoTracking().ToListAsync(cancellationToken); + } + + public async Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default) + { + return await _dbContext.Set().FindAsync(Id, cancellationToken); + } + + public async Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + return await _dbContext.Set().FirstOrDefaultAsync(whereCondition, cancellationToken); + } + + public async Task> GetListAsync(Expression>? whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + var result = _dbContext.Set().AsNoTracking().AsQueryable(); + var elementType = result.ElementType; + var isDateAuditable = typeof(IDateAudited).IsAssignableFrom(elementType); + + if (whereCondition != null) + result = result.Where(whereCondition); + + if (sortCriteria.Any()) + { + bool isFirstSortQuery = true; + foreach (var criteria in sortCriteria) + { + var sortColumnName = criteria.Item1; + var sortDirection = criteria.Item2; + + if (isFirstSortQuery) + { + isFirstSortQuery = false; + result = sortDirection == SortDirection.Ascending ? + result.OrderBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) : + result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + else + { + result = sortDirection == SortDirection.Ascending + ? ((IOrderedQueryable)result).ThenBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) + : ((IOrderedQueryable)result).ThenByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + } + } + else + { + PropertyInfo? propertyInfo = result.GetType().GetGenericArguments()[0].GetProperty(SortColumnConstants.DefaultSortColumn); + if (propertyInfo != null) + result = result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, SortColumnConstants.DefaultSortColumn)); + } + + if (pageIndex == 0 && numberItemsPerPage == 0) + return await result.AsNoTracking().ToListAsync(cancellationToken); + + return await result.Skip((pageIndex + 1) * numberItemsPerPage) + .Take(numberItemsPerPage) + .AsNoTracking() + .ToListAsync(cancellationToken); + } + + public async Task SaveChangeAsync(CancellationToken cancellationToken = default) + { + return await _dbContext.SaveChangesAsync(cancellationToken); + } + + public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) + { + _dbContext.Entry(entity).State = EntityState.Modified; + if (entity is IDateAudited) + { + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + await _dbContext.SaveChangesAsync(cancellationToken); + } + } + + public class EFClassRepository : IEFClassRepository + where TDbContext : DbContext + where TEntity : class + { + protected readonly TDbContext _dbContext; + public EFClassRepository(TDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task AddAsync(TEntity entity, CancellationToken cancellationToken = default) + { + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + await _dbContext.Set().AddAsync(entity, cancellationToken); + return entity; + } + + public async Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) + { + if (entities.FirstOrDefault() is IDateAudited) + { + foreach (var entity in entities) + { + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + } + } + + await _dbContext.Set().AddRangeAsync(entities, cancellationToken); + return entities; + } + + public async Task CountAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + if (whereCondition == null) + return await _dbContext.Set().CountAsync(cancellationToken); + + return await _dbContext.Set().CountAsync(whereCondition, cancellationToken); + } + + public async Task HardDeleteAsync(TEntity entity, CancellationToken cancellationToken = default) + { + await Task.Yield(); + _dbContext.Set().Remove(entity); + } + + public async Task HardDeleteRangeAsync(List entities, CancellationToken cancellationToken = default) + { + await Task.Yield(); + _dbContext.Set().RemoveRange(entities); + } + + public async Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + var result = _dbContext.Set().AsNoTracking().AsQueryable(); + var elementType = result.ElementType; + var isDateAuditable = typeof(IDateAudited).IsAssignableFrom(elementType); + + if (whereCondition != null) + result = result.Where(whereCondition); + + if (sortCriteria.Any()) + { + bool isFirstSortQuery = true; + foreach (var criteria in sortCriteria) + { + var sortColumnName = criteria.Item1; + var sortDirection = criteria.Item2; + + if (isFirstSortQuery) + { + isFirstSortQuery = false; + result = sortDirection == SortDirection.Ascending ? + result.OrderBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) : + result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + else + { + result = sortDirection == SortDirection.Ascending + ? ((IOrderedQueryable)result).ThenBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) + : ((IOrderedQueryable)result).ThenByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + } + } + else + { + PropertyInfo? propertyInfo = result.GetType().GetGenericArguments()[0].GetProperty(SortColumnConstants.DefaultSortColumn); + if (propertyInfo != null) + result = result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, SortColumnConstants.DefaultSortColumn)); + } + + return await result.AsNoTracking().ToListAsync(cancellationToken); + } + + public async Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default) + { + return await _dbContext.Set().FindAsync(Id, cancellationToken); + } + + public async Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + return await _dbContext.Set().FirstOrDefaultAsync(whereCondition, cancellationToken); + } + + public async Task> GetListAsync(Expression> whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + var result = _dbContext.Set().AsNoTracking().AsQueryable(); + var elementType = result.ElementType; + var isDateAuditable = typeof(IDateAudited).IsAssignableFrom(elementType); + + if (whereCondition != null) + result = result.Where(whereCondition); + + if (sortCriteria.Any()) + { + bool isFirstSortQuery = true; + foreach (var criteria in sortCriteria) + { + var sortColumnName = criteria.Item1; + var sortDirection = criteria.Item2; + + if (isFirstSortQuery) + { + isFirstSortQuery = false; + result = sortDirection == SortDirection.Ascending ? + result.OrderBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) : + result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + else + { + result = sortDirection == SortDirection.Ascending + ? ((IOrderedQueryable)result).ThenBy(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)) + : ((IOrderedQueryable)result).ThenByDescending(x => RefectionExtensions.GetPropertyValue(x, sortColumnName)); + } + } + } + else + { + PropertyInfo? propertyInfo = result.GetType().GetGenericArguments()[0].GetProperty(SortColumnConstants.DefaultSortColumn); + if (propertyInfo != null) + result = result.OrderByDescending(x => RefectionExtensions.GetPropertyValue(x, SortColumnConstants.DefaultSortColumn)); + } + + if (pageIndex == 0 && numberItemsPerPage == 0) + return await result.AsNoTracking().ToListAsync(cancellationToken); + + return await result.Skip((pageIndex + 1) * numberItemsPerPage) + .Take(numberItemsPerPage) + .AsNoTracking() + .ToListAsync(cancellationToken); + } + + public async Task SaveChangeAsync(CancellationToken cancellationToken = default) + { + try + { + return await _dbContext.SaveChangesAsync(cancellationToken); + } + catch (Exception ex) + { + return 0; + } + + } + + public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) + { + _dbContext.Entry(entity).State = EntityState.Modified; + if (entity is IDateAudited) + { + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + await _dbContext.SaveChangesAsync(cancellationToken); + } + + public async Task HardDeleteAsync(Expression> predicate, CancellationToken cancellationToken = default) + { + await Task.Yield(); + var entity = await _dbContext.Set().FirstOrDefaultAsync(predicate, cancellationToken); + if (entity != null) + _dbContext.Set().Remove(entity); + } + + public async Task HardDeleteRangeAsync(Expression> predicate, CancellationToken cancellationToken = default) + { + await Task.Yield(); + var entities = await _dbContext.Set().Where(predicate).ToListAsync(cancellationToken); + if (entities.Any()) + _dbContext.Set().RemoveRange(entities); + } + } + + public class EFRepository : IEFRepository + where TDbContext : DbContext + where TEntity : EntityBase + where TModel : class + { + protected readonly TDbContext _dbContext; + private readonly IMapper _mapper; + public EFRepository(TDbContext dbContext, IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task AddAsync(TModel model, CancellationToken cancellationToken = default) + { + var entity = _mapper.Map(model); + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + await _dbContext.Set().AddAsync(entity, cancellationToken); + + return _mapper.Map(entity); + } + + public async Task> AddRangeAsync(IEnumerable models, CancellationToken cancellationToken = default) + { + var entities = _mapper.Map>(models); + if (entities.FirstOrDefault() is IDateAudited) + { + foreach (var entity in entities) + { + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + } + } + + await _dbContext.Set().AddRangeAsync(entities, cancellationToken); + return _mapper.Map>(entities); + } + + public Task CountAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task DeleteAsync(TModel model, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task DeleteRangeAsync(List models, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetListAsync(Expression> whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(TModel model, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + } + + public class EFClassRepository : IEFClassRepository + where TDbContext : DbContext + where TEntity : EntityBase + where TModel : class + { + protected readonly TDbContext _dbContext; + private readonly IMapper _mapper; + public EFClassRepository(TDbContext dbContext, IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task AddAsync(TModel model, CancellationToken cancellationToken = default) + { + var entity = _mapper.Map(model); + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + + await _dbContext.Set().AddAsync(entity, cancellationToken); + + return _mapper.Map(entity); + } + + public async Task> AddRangeAsync(IEnumerable models, CancellationToken cancellationToken = default) + { + var entities = _mapper.Map>(models); + if (entities.FirstOrDefault() is IDateAudited) + { + foreach (var entity in entities) + { + if (entity is IDateAudited) + { + ((IDateAudited)entity).CreatedDate = DateTime.UtcNow; + ((IDateAudited)entity).UpdatedDate = DateTime.UtcNow; + } + + if (entity is ISoftDeleted) + { + ((ISoftDeleted)entity).IsDeleted = false; + ((ISoftDeleted)entity).DeletedAt = null; + } + + if (entity is IActiveEntity) + { + ((IActiveEntity)entity).IsActive = true; + } + } + } + + await _dbContext.Set().AddRangeAsync(entities, cancellationToken); + return _mapper.Map>(entities); + } + + public Task CountAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task DeleteAsync(TModel model, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task DeleteRangeAsync(List models, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetListAsync(Expression> whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(TModel model, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/Extensions/RefectionExtensions.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/Extensions/RefectionExtensions.cs new file mode 100644 index 00000000..a101d98e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/Extensions/RefectionExtensions.cs @@ -0,0 +1,19 @@ +using System.Reflection; + +namespace PlanningBook.Repository.EF.Extensions +{ + public static class RefectionExtensions + { + public static object? GetPropertyValue(object obj, string property) + { + if (!string.IsNullOrEmpty(property)) + { + PropertyInfo? propertyInfo = obj.GetType().GetProperty(property, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + if (propertyInfo != null) + return propertyInfo.GetValue(obj, null); + return null; + } + return null; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/IEFRepository.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/IEFRepository.cs new file mode 100644 index 00000000..814a63ab --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/IEFRepository.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Repository.EF +{ + public interface IEFRepository : IRepository + where TDbContext : DbContext + where TEntity : EntityBase + { + } + + public interface IEFClassRepository : IClassRepository + where TDbContext : DbContext + where TEntity : class + { + } + + public interface IEFRepository : IRepository + where TDbContext : DbContext + where TEntity : EntityBase + where TModel : class + { + } + + public interface IEFClassRepository : IClassRepository + where TDbContext : DbContext + where TEntity : class + where TModel : class + { + } + +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/PlanningBook.Repository.EF.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/PlanningBook.Repository.EF.csproj new file mode 100644 index 00000000..51ec178e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/Planning_book.Repository.EF/PlanningBook.Repository.EF.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/SupportCallServerToServer/Class1.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/SupportCallServerToServer/Class1.cs new file mode 100644 index 00000000..7b202c41 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/SupportCallServerToServer/Class1.cs @@ -0,0 +1,7 @@ +namespace SupportCallServerToServer +{ + public class Class1 + { + + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/SupportCallServerToServer/SupportCallServerToServer.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/SupportCallServerToServer/SupportCallServerToServer.csproj new file mode 100644 index 00000000..fa71b7ae --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/BuildingBlocks/Presitance/SupportCallServerToServer/SupportCallServerToServer.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Documents/How_To_Run_Migration_For_Each_Service.txt.txt b/.Net/Payment/Stripe - Phat.Tran/Servers/Documents/How_To_Run_Migration_For_Each_Service.txt.txt new file mode 100644 index 00000000..11bf179d --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Documents/How_To_Run_Migration_For_Each_Service.txt.txt @@ -0,0 +1,10 @@ +Step 1: Go to service folder you want to run migration (../Services/) + +Step 2: +- Open package manager console and choose Default project: .Infrastructure + +*IF you make some change with database structure: +Add-Migration -StartupProject PlanningBook..API -Project PlanningBook..Infrastructure -v + +*Apply new change for databse: +update-database -s PlanningBook..API \ No newline at end of file diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Contants/CustomClaimTypes.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Contants/CustomClaimTypes.cs new file mode 100644 index 00000000..48718808 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Contants/CustomClaimTypes.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Contants +{ + public static class CustomClaimTypes + { + public const string AccountId = "id"; + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Contants/PlanningBook.Contants.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Contants/PlanningBook.Contants.csproj new file mode 100644 index 00000000..fa71b7ae --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Contants/PlanningBook.Contants.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Extensions/AuthExtensions.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Extensions/AuthExtensions.cs new file mode 100644 index 00000000..792be3a6 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Extensions/AuthExtensions.cs @@ -0,0 +1,20 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Http; + +namespace PlanningBook.Extensions +{ + public static class AuthExtensions + { + public static Guid? GetCurrentAccountId(this ClaimsPrincipal account) + { + var userId = account.FindFirstValue(ClaimTypes.NameIdentifier); + return string.IsNullOrWhiteSpace(userId) ? null : Guid.Parse(userId); + } + + public static string? GetCurrentJwtToken(this HttpRequest request) + { + var authHeader = request.Headers["Authorization"].FirstOrDefault(); + return authHeader?.StartsWith("Bearer ") == true ? authHeader.Substring("Bearer ".Length).Trim() : null; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Extensions/PlanningBook.Extensions.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Extensions/PlanningBook.Extensions.csproj new file mode 100644 index 00000000..468961a2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/PlanningBook.Extensions/PlanningBook.Extensions.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/BaseResult.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/BaseResult.cs new file mode 100644 index 00000000..4c7899c0 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/BaseResult.cs @@ -0,0 +1,10 @@ +namespace PlanningBook.Domain +{ + public class BaseResult + { + public TData? Data { get; set; } + public bool IsSuccess { get; set; } + public string? ErrorCode { get; set; } + public string? ErrorMessage { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/CommandExecutor.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/CommandExecutor.cs new file mode 100644 index 00000000..16a7b36f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/CommandExecutor.cs @@ -0,0 +1,43 @@ +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Domain +{ + public class CommandExecutor : ICommandExecutor + { + private readonly IServiceProvider _serviceProvider; + //TODO: Implement Logger + //private readonly ILogger _logger; + + public CommandExecutor(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + //_logger = (ILogger)_serviceProvider.GetService(typeof(ILogger)); + } + + public Task ExecuteAsync(ICommand command, CancellationToken cancellationToken = default) + { + var errorMessage = string.Empty; + + var htype = typeof(ICommandHandler<,>).MakeGenericType(command.GetType(), typeof(TResult)); + if(htype == null) + { + errorMessage = $"Handler for command type {command.GetType().Name} and result type {typeof(TResult).Name} not found htype."; + //TODO: Implement Logger + //_logger.LogTrace(message); + throw new InvalidOperationException(errorMessage); + } + + dynamic handler = _serviceProvider.GetService(htype); + if (handler == null) { + errorMessage = $"Handler for command type {command.GetType().Name} and result type {typeof(TResult).Name} not found handler."; + //TODO: Implement Logger + //_logger.LogTrace(message); + throw new InvalidOperationException(errorMessage); + } + + //TODO: Implement Logger + //_logger.LogTrace($"[Command Executor] [{handler.GetType().Name}] {JsonConvert.SerializeObject(command)}"); + return ((dynamic)handler).HandleAsync((dynamic)command); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/CommandResult.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/CommandResult.cs new file mode 100644 index 00000000..0a1c3e4a --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/CommandResult.cs @@ -0,0 +1,24 @@ +namespace PlanningBook.Domain +{ + public class CommandResult : BaseResult + { + public static CommandResult Success(TData? data) + { + return new CommandResult() + { + IsSuccess = true, + Data = data + }; + } + + public static CommandResult Failure(string? errorCode = null, string? errorMessages = null) + { + return new CommandResult() + { + IsSuccess = false, + ErrorCode = errorCode, + ErrorMessage = errorMessages + }; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Constant/SortColumnConstants.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Constant/SortColumnConstants.cs new file mode 100644 index 00000000..d48e5be8 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Constant/SortColumnConstants.cs @@ -0,0 +1,9 @@ +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Domain.Constant +{ + public static class SortColumnConstants + { + public const string DefaultSortColumn = nameof(IDateAudited.UpdatedDate); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/EntityBase.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/EntityBase.cs new file mode 100644 index 00000000..31b3adae --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/EntityBase.cs @@ -0,0 +1,9 @@ +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Domain +{ + public abstract class EntityBase : IEntityBase + { + public TPrimaryKey Id { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Enums/SortDirection.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Enums/SortDirection.cs new file mode 100644 index 00000000..78bdd44e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Enums/SortDirection.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Domain.Enums +{ + public enum SortDirection + { + Ascending, + Descending, + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IActiveEntity.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IActiveEntity.cs new file mode 100644 index 00000000..9653a757 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IActiveEntity.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IActiveEntity + { + public bool IsActive { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IAuthorAudited.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IAuthorAudited.cs new file mode 100644 index 00000000..e0ebb697 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IAuthorAudited.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IAuthorAudited + { + TPrimaryKey? CreatedBy { get; set; } + TPrimaryKey? UpdatedBy { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommand.cs new file mode 100644 index 00000000..84daa50b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommand.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface ICommand + { + public ValidationResult GetValidationResult(); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommandExecutor.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommandExecutor.cs new file mode 100644 index 00000000..488ae11e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommandExecutor.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface ICommandExecutor + { + Task ExecuteAsync(ICommand command, CancellationToken cancellationToken = default); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommandHandler.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommandHandler.cs new file mode 100644 index 00000000..d0d93b38 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ICommandHandler.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface ICommandHandler where TCommand : ICommand + { + Task HandleAsync(TCommand command, CancellationToken cancellationToken = default); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IDateAudited.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IDateAudited.cs new file mode 100644 index 00000000..9f35b517 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IDateAudited.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IDateAudited + { + DateTime? CreatedDate { get; set; } + DateTime? UpdatedDate { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IEntityBase.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IEntityBase.cs new file mode 100644 index 00000000..ff30a2c7 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IEntityBase.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IEntityBase + { + TPrimaryKey Id { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IFullAudited.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IFullAudited.cs new file mode 100644 index 00000000..68b265af --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IFullAudited.cs @@ -0,0 +1,6 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IFullAudited : IDateAudited, IAuthorAudited + { + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQuery.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQuery.cs new file mode 100644 index 00000000..5cb266de --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQuery.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IQuery + { + public ValidationResult GetValidationResult(); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQueryExecutor.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQueryExecutor.cs new file mode 100644 index 00000000..e1a272df --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQueryExecutor.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IQueryExecutor + { + Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQueryHandler.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQueryHandler.cs new file mode 100644 index 00000000..08f5a5bd --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IQueryHandler.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IQueryHandler where TQuery : IQuery + { + Task HandleAsync(TQuery query, CancellationToken cancellationToken = default); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IRepository.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IRepository.cs new file mode 100644 index 00000000..e80669b1 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IRepository.cs @@ -0,0 +1,79 @@ +using PlanningBook.Domain.Enums; +using System.Linq.Expressions; + +namespace PlanningBook.Domain.Interfaces +{ + // Use for get data as Original Format + public interface IRepository : IUnitOfWork + where TEntity : EntityBase + { + Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default); + Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default); + Task> GetListAsync(Expression> whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task CountAsync(Expression>? whereCondition = null, CancellationToken cancellationToken = default); + Task AddAsync(TEntity entity, CancellationToken cancellationToken = default); + Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default); + Task DeleteRangeAsync(List entities, CancellationToken cancellationToken = default); + + // TODO: BulkAdd, BulkUpdate, BulkDelete + } + + public interface IClassRepository : IUnitOfWork + where TEntity : class + { + Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default); + Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default); + Task> GetListAsync(Expression> whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task CountAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default); + Task AddAsync(TEntity entity, CancellationToken cancellationToken = default); + Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + Task HardDeleteAsync(TEntity entity, CancellationToken cancellationToken = default); + Task HardDeleteAsync(Expression> predicate, CancellationToken cancellationToken = default); + Task HardDeleteRangeAsync(List entities, CancellationToken cancellationToken = default); + Task HardDeleteRangeAsync(Expression> predicate, CancellationToken cancellationToken = default); + + // TODO: BulkAdd, BulkUpdate, BulkDelete + } + + // Use for get data as other model (Ex: ReadOnly Model, View Model, etc) + public interface IRepository + where TEntity : EntityBase + where TModel : class + { + Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default); + Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default); + Task> GetListAsync(Expression> whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task CountAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default); + Task AddAsync(TModel model, CancellationToken cancellationToken = default); + Task> AddRangeAsync(IEnumerable models, CancellationToken cancellationToken = default); + Task UpdateAsync(TModel model, CancellationToken cancellationToken = default); + Task DeleteAsync(TModel model, CancellationToken cancellationToken = default); + Task DeleteRangeAsync(List models, CancellationToken cancellationToken = default); + + // TODO: BulkAdd, BulkUpdate, BulkDelete + } + + public interface IClassRepository + where TEntity : class + where TModel : class + { + Task> GetAsync(Expression> whereCondition, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid Id, CancellationToken cancellationToken = default); + Task GetFirstAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default); + Task> GetListAsync(Expression> whereCondition, int pageIndex = 0, int numberItemsPerPage = 0, List> sortCriteria = null, CancellationToken cancellationToken = default); + Task CountAsync(Expression> whereCondition = null, CancellationToken cancellationToken = default); + Task AddAsync(TModel model, CancellationToken cancellationToken = default); + Task> AddRangeAsync(IEnumerable models, CancellationToken cancellationToken = default); + Task UpdateAsync(TModel model, CancellationToken cancellationToken = default); + Task DeleteAsync(TModel model, CancellationToken cancellationToken = default); + Task DeleteRangeAsync(List models, CancellationToken cancellationToken = default); + + // TODO: BulkAdd, BulkUpdate, BulkDelete + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ISoftDeleted.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ISoftDeleted.cs new file mode 100644 index 00000000..98af5ec7 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/ISoftDeleted.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface ISoftDeleted + { + public bool IsDeleted { get; set; } + public DateTime? DeletedAt { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IUnitOfWork.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IUnitOfWork.cs new file mode 100644 index 00000000..e9fd4c56 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/Interfaces/IUnitOfWork.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Domain.Interfaces +{ + public interface IUnitOfWork + { + Task SaveChangeAsync(CancellationToken cancellationToken = default); + //TODO: BulkSaveChange + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/ModelBase.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/ModelBase.cs new file mode 100644 index 00000000..c4b1d90b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/ModelBase.cs @@ -0,0 +1,6 @@ +namespace PlanningBook.Domain +{ + public class ModelBase : EntityBase + { + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PageResult.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PageResult.cs new file mode 100644 index 00000000..c614d0a9 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PageResult.cs @@ -0,0 +1,11 @@ +namespace PlanningBook.Domain +{ + public class PageResult + { + public int TotalItems { get; set; } + public int CurrentPage { get; set; } + public int NumberItemsPerPage { get; set; } + public int TotalPage => (TotalItems / NumberItemsPerPage) + ((TotalItems % NumberItemsPerPage) > 0 ? 1 : 0); + public IEnumerable Data { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PagingFilterCriteria.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PagingFilterCriteria.cs new file mode 100644 index 00000000..89253ade --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PagingFilterCriteria.cs @@ -0,0 +1,13 @@ +using PlanningBook.Domain.Constant; +using PlanningBook.Domain.Enums; + +namespace PlanningBook.Domain +{ + public class PagingFilterCriteria + { + public int PageIndex { get; set; } + public int NumberItemsPerPage { get; set; } + public List>? SortCriteria { get; set; } + public string? QueryText { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PlanningBook.Domain.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PlanningBook.Domain.csproj new file mode 100644 index 00000000..fa71b7ae --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/PlanningBook.Domain.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/QueryExecutor.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/QueryExecutor.cs new file mode 100644 index 00000000..5a646830 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/QueryExecutor.cs @@ -0,0 +1,40 @@ +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Domain +{ + public class QueryExecutor : IQueryExecutor + { + private readonly IServiceProvider _serviceProvider; + //TODO: Implement Logger + //private readonly ILogger _logger; + public QueryExecutor(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + //_logger = (ILogger)_serviceProvider.GetService(typeof(ILogger)); + } + public Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default) + { + var errorMessage = string.Empty; + var htype = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult)); + if (htype == null) + { + errorMessage = $"Handler for query type {query.GetType().Name} and result type {typeof(TResult).Name} not found htype."; + //TODO: Implement Logger + //_logger.LogTrace(message); + throw new InvalidOperationException(errorMessage); + } + + dynamic handler = _serviceProvider.GetService(htype); + if (handler == null) + { + errorMessage = $"Handler for query type {query.GetType().Name} and result type {typeof(TResult).Name} not found handler."; + //TODO: Implement Logger + //_logger.LogTrace(message); + throw new InvalidOperationException(errorMessage); + } + + //_logger.LogTrace($"[Query Executor] [{handler.GetType().Name}] {JsonConvert.SerializeObject(query)}"); + return handler.HandleAsync((dynamic)query); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/QueryResult.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/QueryResult.cs new file mode 100644 index 00000000..b82fbdc2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/QueryResult.cs @@ -0,0 +1,24 @@ +namespace PlanningBook.Domain +{ + public class QueryResult : BaseResult + { + public static QueryResult Success(TData? data) + { + return new QueryResult() + { + IsSuccess = true, + Data = data + }; + } + + public static QueryResult Failure(string? errorCode = null, string? errorMessages = null) + { + return new QueryResult() + { + IsSuccess = false, + ErrorCode = errorCode, + ErrorMessage = errorMessages + }; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/READ_ME.txt b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/READ_ME.txt new file mode 100644 index 00000000..e69de29b diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/ValidationResult.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/ValidationResult.cs new file mode 100644 index 00000000..dadd84ce --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Domain/ValidationResult.cs @@ -0,0 +1,24 @@ +namespace PlanningBook.Domain +{ + public class ValidationResult + { + public bool IsValid { get; set; } + public List? ErrorCodes { get; set; } + public List? Messages { get; set; } + + public static ValidationResult Success() + { + return new ValidationResult() { IsValid = true }; + } + + public static ValidationResult Failure(List? ErrorCodes = null, List? Messages = null) + { + return new ValidationResult() + { + IsValid = false, + ErrorCodes = ErrorCodes, + Messages = Messages + }; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/AccountRole.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/AccountRole.cs new file mode 100644 index 00000000..a163c8aa --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/AccountRole.cs @@ -0,0 +1,13 @@ +namespace PlanningBook.Enums +{ + // Any changes should sync with PersonRole & RoleType(IdentityService) + // Start from 1 -> 2000 + public enum AccountRole + { + // Internal start from 1 -> 1000 + SysAdmin = 1, + + // External start from 1001 -> 2000 + User = 1001 + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/PersonRole.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/PersonRole.cs new file mode 100644 index 00000000..81f88126 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/PersonRole.cs @@ -0,0 +1,12 @@ +namespace PlanningBook.Enums +{ + // Any changes should sync with AccountRole & RoleType(IdentityService) + // Start from 3000 -> 5000 + public enum PersonRole + { + // Internal start from 3000 -> 4000 + Staff = 3000, + // External start from 4001 -> 5000 + EndUser = 4001 + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/PlanningBook.Enums.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/PlanningBook.Enums.csproj new file mode 100644 index 00000000..7efe34c7 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Frameworks/Planning_book.Enums/PlanningBook.Enums.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/PlanningBook.sln b/.Net/Payment/Stripe - Phat.Tran/Servers/PlanningBook.sln new file mode 100644 index 00000000..6317228b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/PlanningBook.sln @@ -0,0 +1,156 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34916.146 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{D9E8AF1F-E38F-4E01-96EA-6018DA7051BE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{703AD3BC-82D1-4758-8584-FB73DD5A7993}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{D400946F-593F-43C6-8188-014BA4358D55}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Domain", "Frameworks\Planning_book.Domain\PlanningBook.Domain.csproj", "{7F49CD2F-55EF-4383-918D-463A201E15E4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Enums", "Frameworks\Planning_book.Enums\PlanningBook.Enums.csproj", "{A52F67C9-373C-491A-85FC-381B1ED34BD6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presitance", "Presitance", "{BE6B8E6F-9A1D-4830-9710-3CCF524FEAC1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Repository.EF", "BuildingBlocks\Presitance\Planning_book.Repository.EF\PlanningBook.Repository.EF.csproj", "{D1D6D065-FBDE-4B70-BA1B-0C786BCEA528}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{EE0B4777-24C6-4E74-8471-9F98158B1C5F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Identity.API", "Services\Identity\PlanningBook.Identity.API\PlanningBook.Identity.API.csproj", "{0E5C7008-7162-4581-A841-B920947D8DC7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Identity.Application", "Services\Identity\PlanningBook.Identity.Application\PlanningBook.Identity.Application.csproj", "{F931949D-4213-4AD9-9740-C3BFE3C13C0A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Identity.Infrastructure", "Services\Identity\PlanningBook.Identity.Infrastructure\PlanningBook.Identity.Infrastructure.csproj", "{482612B1-114A-4FFE-91DD-3B78CEB529E1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.DBEngine", "BuildingBlocks\Presitance\PlanningBook.DBEngine\PlanningBook.DBEngine.csproj", "{474F19C7-8147-4140-A487-3A714BC40959}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Users", "Users", "{E2E9D153-A0DA-401F-BD83-B0A084277755}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Users.Application", "Services\Users\PlanningBook.Users.Application\PlanningBook.Users.Application.csproj", "{6A33BD29-DDE3-4802-9887-18971E4613F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Users.Infrastructure", "Services\Users\PlanningBook.Users.Infrastructure\PlanningBook.Users.Infrastructure.csproj", "{4D535B8D-06FF-43AA-BB2D-F453EF23F55F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Users.API", "Services\Users\PlanningBook.Users.API\PlanningBook.Users.API.csproj", "{774B915B-705D-4894-BD2E-57166272DA1E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Contants", "Frameworks\PlanningBook.Contants\PlanningBook.Contants.csproj", "{A7176048-049F-4E7A-A9A7-49A5FCD06DB9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Extensions", "Frameworks\PlanningBook.Extensions\PlanningBook.Extensions.csproj", "{7965B0C9-41BB-4B87-BFF1-CD40D0BF3C15}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{49FE407B-8605-48CF-B8F7-687477590ABC}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SupportCallServerToServer", "BuildingBlocks\Presitance\SupportCallServerToServer\SupportCallServerToServer.csproj", "{3DF19762-9694-48E6-B928-15346FC2BEF4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Themes", "Themes", "{51DA1DB8-F462-4C6B-9CA9-0D680EC5B866}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Themes.Application", "Services\Themes\PlanningBook.Themes.Application\PlanningBook.Themes.Application.csproj", "{0215126B-C6B4-4D21-874C-F5B925E52C15}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Themes.Infrastructure", "Services\Themes\PlanningBook.Themes.Infrastructure\PlanningBook.Themes.Infrastructure.csproj", "{035F462B-0BFE-4058-8C50-087563F4C6EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlanningBook.Themes.API", "Services\Themes\PlanningBook.Themes.API\PlanningBook.Themes.API.csproj", "{FCF1811E-1F81-4B8A-A6EE-577EF7525EF4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7F49CD2F-55EF-4383-918D-463A201E15E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F49CD2F-55EF-4383-918D-463A201E15E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F49CD2F-55EF-4383-918D-463A201E15E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F49CD2F-55EF-4383-918D-463A201E15E4}.Release|Any CPU.Build.0 = Release|Any CPU + {A52F67C9-373C-491A-85FC-381B1ED34BD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A52F67C9-373C-491A-85FC-381B1ED34BD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A52F67C9-373C-491A-85FC-381B1ED34BD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A52F67C9-373C-491A-85FC-381B1ED34BD6}.Release|Any CPU.Build.0 = Release|Any CPU + {D1D6D065-FBDE-4B70-BA1B-0C786BCEA528}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1D6D065-FBDE-4B70-BA1B-0C786BCEA528}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1D6D065-FBDE-4B70-BA1B-0C786BCEA528}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1D6D065-FBDE-4B70-BA1B-0C786BCEA528}.Release|Any CPU.Build.0 = Release|Any CPU + {0E5C7008-7162-4581-A841-B920947D8DC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E5C7008-7162-4581-A841-B920947D8DC7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E5C7008-7162-4581-A841-B920947D8DC7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E5C7008-7162-4581-A841-B920947D8DC7}.Release|Any CPU.Build.0 = Release|Any CPU + {F931949D-4213-4AD9-9740-C3BFE3C13C0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F931949D-4213-4AD9-9740-C3BFE3C13C0A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F931949D-4213-4AD9-9740-C3BFE3C13C0A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F931949D-4213-4AD9-9740-C3BFE3C13C0A}.Release|Any CPU.Build.0 = Release|Any CPU + {482612B1-114A-4FFE-91DD-3B78CEB529E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {482612B1-114A-4FFE-91DD-3B78CEB529E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {482612B1-114A-4FFE-91DD-3B78CEB529E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {482612B1-114A-4FFE-91DD-3B78CEB529E1}.Release|Any CPU.Build.0 = Release|Any CPU + {474F19C7-8147-4140-A487-3A714BC40959}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {474F19C7-8147-4140-A487-3A714BC40959}.Debug|Any CPU.Build.0 = Debug|Any CPU + {474F19C7-8147-4140-A487-3A714BC40959}.Release|Any CPU.ActiveCfg = Release|Any CPU + {474F19C7-8147-4140-A487-3A714BC40959}.Release|Any CPU.Build.0 = Release|Any CPU + {6A33BD29-DDE3-4802-9887-18971E4613F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A33BD29-DDE3-4802-9887-18971E4613F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A33BD29-DDE3-4802-9887-18971E4613F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A33BD29-DDE3-4802-9887-18971E4613F4}.Release|Any CPU.Build.0 = Release|Any CPU + {4D535B8D-06FF-43AA-BB2D-F453EF23F55F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D535B8D-06FF-43AA-BB2D-F453EF23F55F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D535B8D-06FF-43AA-BB2D-F453EF23F55F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D535B8D-06FF-43AA-BB2D-F453EF23F55F}.Release|Any CPU.Build.0 = Release|Any CPU + {774B915B-705D-4894-BD2E-57166272DA1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {774B915B-705D-4894-BD2E-57166272DA1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {774B915B-705D-4894-BD2E-57166272DA1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {774B915B-705D-4894-BD2E-57166272DA1E}.Release|Any CPU.Build.0 = Release|Any CPU + {A7176048-049F-4E7A-A9A7-49A5FCD06DB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7176048-049F-4E7A-A9A7-49A5FCD06DB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7176048-049F-4E7A-A9A7-49A5FCD06DB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7176048-049F-4E7A-A9A7-49A5FCD06DB9}.Release|Any CPU.Build.0 = Release|Any CPU + {7965B0C9-41BB-4B87-BFF1-CD40D0BF3C15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7965B0C9-41BB-4B87-BFF1-CD40D0BF3C15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7965B0C9-41BB-4B87-BFF1-CD40D0BF3C15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7965B0C9-41BB-4B87-BFF1-CD40D0BF3C15}.Release|Any CPU.Build.0 = Release|Any CPU + {3DF19762-9694-48E6-B928-15346FC2BEF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DF19762-9694-48E6-B928-15346FC2BEF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DF19762-9694-48E6-B928-15346FC2BEF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DF19762-9694-48E6-B928-15346FC2BEF4}.Release|Any CPU.Build.0 = Release|Any CPU + {0215126B-C6B4-4D21-874C-F5B925E52C15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0215126B-C6B4-4D21-874C-F5B925E52C15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0215126B-C6B4-4D21-874C-F5B925E52C15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0215126B-C6B4-4D21-874C-F5B925E52C15}.Release|Any CPU.Build.0 = Release|Any CPU + {035F462B-0BFE-4058-8C50-087563F4C6EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {035F462B-0BFE-4058-8C50-087563F4C6EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {035F462B-0BFE-4058-8C50-087563F4C6EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {035F462B-0BFE-4058-8C50-087563F4C6EE}.Release|Any CPU.Build.0 = Release|Any CPU + {FCF1811E-1F81-4B8A-A6EE-577EF7525EF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCF1811E-1F81-4B8A-A6EE-577EF7525EF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCF1811E-1F81-4B8A-A6EE-577EF7525EF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCF1811E-1F81-4B8A-A6EE-577EF7525EF4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {7F49CD2F-55EF-4383-918D-463A201E15E4} = {703AD3BC-82D1-4758-8584-FB73DD5A7993} + {A52F67C9-373C-491A-85FC-381B1ED34BD6} = {703AD3BC-82D1-4758-8584-FB73DD5A7993} + {BE6B8E6F-9A1D-4830-9710-3CCF524FEAC1} = {D9E8AF1F-E38F-4E01-96EA-6018DA7051BE} + {D1D6D065-FBDE-4B70-BA1B-0C786BCEA528} = {BE6B8E6F-9A1D-4830-9710-3CCF524FEAC1} + {EE0B4777-24C6-4E74-8471-9F98158B1C5F} = {D400946F-593F-43C6-8188-014BA4358D55} + {0E5C7008-7162-4581-A841-B920947D8DC7} = {EE0B4777-24C6-4E74-8471-9F98158B1C5F} + {F931949D-4213-4AD9-9740-C3BFE3C13C0A} = {EE0B4777-24C6-4E74-8471-9F98158B1C5F} + {482612B1-114A-4FFE-91DD-3B78CEB529E1} = {EE0B4777-24C6-4E74-8471-9F98158B1C5F} + {474F19C7-8147-4140-A487-3A714BC40959} = {BE6B8E6F-9A1D-4830-9710-3CCF524FEAC1} + {E2E9D153-A0DA-401F-BD83-B0A084277755} = {D400946F-593F-43C6-8188-014BA4358D55} + {6A33BD29-DDE3-4802-9887-18971E4613F4} = {E2E9D153-A0DA-401F-BD83-B0A084277755} + {4D535B8D-06FF-43AA-BB2D-F453EF23F55F} = {E2E9D153-A0DA-401F-BD83-B0A084277755} + {774B915B-705D-4894-BD2E-57166272DA1E} = {E2E9D153-A0DA-401F-BD83-B0A084277755} + {A7176048-049F-4E7A-A9A7-49A5FCD06DB9} = {703AD3BC-82D1-4758-8584-FB73DD5A7993} + {7965B0C9-41BB-4B87-BFF1-CD40D0BF3C15} = {703AD3BC-82D1-4758-8584-FB73DD5A7993} + {3DF19762-9694-48E6-B928-15346FC2BEF4} = {BE6B8E6F-9A1D-4830-9710-3CCF524FEAC1} + {51DA1DB8-F462-4C6B-9CA9-0D680EC5B866} = {D400946F-593F-43C6-8188-014BA4358D55} + {0215126B-C6B4-4D21-874C-F5B925E52C15} = {51DA1DB8-F462-4C6B-9CA9-0D680EC5B866} + {035F462B-0BFE-4058-8C50-087563F4C6EE} = {51DA1DB8-F462-4C6B-9CA9-0D680EC5B866} + {FCF1811E-1F81-4B8A-A6EE-577EF7525EF4} = {51DA1DB8-F462-4C6B-9CA9-0D680EC5B866} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {691967B8-AD92-4E45-AFF4-282A1C53BF8B} + EndGlobalSection +EndGlobal diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/PlanningBook.APIGateway.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/PlanningBook.APIGateway.csproj new file mode 100644 index 00000000..7015e9b9 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/PlanningBook.APIGateway.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/PlanningBook.APIGateway.http b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/PlanningBook.APIGateway.http new file mode 100644 index 00000000..492acc07 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/PlanningBook.APIGateway.http @@ -0,0 +1,6 @@ +@PlanningBook.APIGateway_HostAddress = http://localhost:5019 + +GET {{PlanningBook.APIGateway_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/Program.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/Program.cs new file mode 100644 index 00000000..bb04eb2d --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/Program.cs @@ -0,0 +1,44 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}) +.WithName("GetWeatherForecast") +.WithOpenApi(); + +app.Run(); + +internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/Properties/launchSettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/Properties/launchSettings.json new file mode 100644 index 00000000..54ba3cc5 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:19864", + "sslPort": 44334 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5019", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7257;http://localhost:5019", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/appsettings.Development.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/appsettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/APIGateway/PlanningBook.APIGateway/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Controllers/ClientAccountsController.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Controllers/ClientAccountsController.cs new file mode 100644 index 00000000..7abcdf2c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Controllers/ClientAccountsController.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Extensions; +using PlanningBook.Identity.Application.Accounts.Commands; +using PlanningBook.Identity.Application.ClientAccounts.Commands; +using PlanningBook.Identity.Application.ClientAccounts.Commands.CommandResults; + +namespace PlanningBook.Identity.API.Controllers +{ + [ApiController] + [Route("identity")] + public class ClientAccountsController( + IQueryExecutor _queryExecutor, + ICommandExecutor _commandExecutor, + HttpClient _httpClient + ) : ControllerBase + { + + [AllowAnonymous] + [HttpPost("sign-up")] + public async Task>> SignUp([FromBody] SignUpClientAccountCommand command) + { + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + + [AllowAnonymous] + [HttpPost("sign-in")] + public async Task>> SignIn([FromBody] SignInClientAccountCommand command) + { + + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + + [Authorize(AuthenticationSchemes = "Bearer")] + [HttpPost("SignOut")] + public async Task>> SignOut() + { + //var token = HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); + var command = new SignOutClientAccountCommand() + { + AccountId = User.GetCurrentAccountId(), + Token = Request.GetCurrentJwtToken() + }; + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Controllers/HealthCheckController.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Controllers/HealthCheckController.cs new file mode 100644 index 00000000..e1b92955 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Controllers/HealthCheckController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Identity.API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class HealthCheckController : ControllerBase + { + private readonly IQueryExecutor _queryExecutor; + private readonly ICommandExecutor _commandExecutor; + + public HealthCheckController( + IQueryExecutor queryExecutor, + ICommandExecutor commandExecutor) + { + _queryExecutor = queryExecutor; + _commandExecutor = commandExecutor; + } + + [AllowAnonymous] + [HttpGet("NonAuth")] + public ActionResult NoAuth() + { + return Ok("Still Live"); + } + + [HttpGet("HasAuth")] + [Authorize(AuthenticationSchemes = "Bearer")] + public ActionResult HasAuth() + { + return Ok("Still Live"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Extensions/IServiceCollectionExtensions.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 00000000..a6d79cba --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,50 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Identity.Application.Accounts.Commands; +using PlanningBook.Identity.Application.ClientAccounts.Commands; +using PlanningBook.Identity.Application.ClientAccounts.Commands.CommandResults; +using PlanningBook.Identity.Application.Helpers; +using PlanningBook.Identity.Application.Helpers.Interfaces; +using PlanningBook.Identity.Application.Providers; +using PlanningBook.Identity.Application.Providers.Interfaces; +using PlanningBook.Repository.EF; + +namespace PlanningBook.Identity.API.Extensions +{ + public static class IServiceCollectionExtensions + { + public static IServiceCollection AddServices(this IServiceCollection services) + { + #region Add Services & Helpers + services.AddSingleton(); + services.AddSingleton(); + #endregion Add Services & Helpers + + #region Add Repositories + services.AddScoped(typeof(IEFRepository<,,>), typeof(EFRepository<,,>)); + services.AddScoped(typeof(IEFRepository<,,,>), typeof(EFRepository<,,,>)); + services.AddScoped(typeof(IEFClassRepository<,,>), typeof(EFClassRepository<,,>)); + services.AddScoped(typeof(IEFClassRepository<,,,>), typeof(EFClassRepository<,,,>)); + #endregion Add Repositories + + return services; + } + + public static IServiceCollection RegistryCommandQueryExecutor(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient(); + services.AddTransient(); + return services; + } + + public static IServiceCollection RegistryAccountModule(this IServiceCollection services, IConfiguration configuration) + { + services.AddScoped>, ChangePasswordClientAccountCommandHandler>(); + services.AddScoped>, SignInClientAccountCommandHandler>(); + services.AddScoped>, SignOutClientAccountCommandHandler>(); + services.AddScoped>, SignUpClientAccountCommandHandler>(); + + return services; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/PlanningBook.Identity.API.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/PlanningBook.Identity.API.csproj new file mode 100644 index 00000000..ee8f6e40 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/PlanningBook.Identity.API.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/PlanningBook.Identity.API.http b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/PlanningBook.Identity.API.http new file mode 100644 index 00000000..36d8f61e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/PlanningBook.Identity.API.http @@ -0,0 +1,6 @@ +@PlanningBook.Identity.API_HostAddress = http://localhost:5002 + +GET {{PlanningBook.Identity.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Program.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Program.cs new file mode 100644 index 00000000..7543199d --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Program.cs @@ -0,0 +1,155 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Identity; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using PlanningBook.Identity.API.Extensions; +using PlanningBook.Identity.Infrastructure; +using PlanningBook.Identity.Infrastructure.Entities; +using System.Net; +using System.Text; + +var builder = WebApplication.CreateBuilder(args); +var configuration = builder.Configuration; + +// Add services to the container. + +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowSpecificOrigins", builder => + { + builder.WithOrigins("http://localhost:4200") + .AllowAnyHeader() + .AllowAnyMethod(); + }); +}); + +builder.Services.AddControllers(); + +#region Add DbContexts +builder.Services.AddPBIdentityDbContext(configuration); +#endregion Add DbContexts + +#region Add Services +builder.Services + .AddServices() + .RegistryCommandQueryExecutor(configuration) + .RegistryAccountModule(configuration); +#endregion Add Services + +#region Add Identity +builder.Services.AddIdentityCore(o => +{ + o.Password.RequiredLength = 8; +}) +.AddRoles() +.AddEntityFrameworkStores() +.AddSignInManager>() +.AddUserManager>() +.AddDefaultTokenProviders(); + +builder.Services.AddAuthentication(o => +{ + o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) + .AddJwtBearer(o => + { + o.SaveToken = true; + o.TokenValidationParameters = new TokenValidationParameters() + { + SaveSigninToken = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])), + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero, + ValidateIssuer = true, + ValidIssuer = builder.Configuration["Jwt:Issuer"], + ValidateAudience = true, + ValidAudience = builder.Configuration["Jwt:Audience"], + }; + }); +builder.Services.AddAuthorization(); +#endregion Add Identity + +#region Add Swagger +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen( + c => + { + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n + Enter 'Bearer' [space] and then your token in the text input below. + \r\n\r\nExample: 'Bearer 12345abcdef'", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header, + + }, + new List() + } + }); + } +); +#endregion Add Swagger + +#region Add Other services +builder.Services.AddHttpClient("Person", client => +{ + client.BaseAddress = new Uri("http://localhost:5002/api/"); + client.DefaultRequestHeaders.Add("Accept", "application/json"); +}); +#endregion Add Other services + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +// TODO: Handler action when token expired +app.Use(async (context, next) => +{ + await next(); + + if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized) // 401 + { + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync("NO"); + } +}); + +app.UseHttpsRedirection(); + +// Auto generate API for authen +//app.MapIdentityApi(); + +app.UseCors("AllowSpecificOrigins"); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Properties/launchSettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Properties/launchSettings.json new file mode 100644 index 00000000..887c3d87 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14714", + "sslPort": 44365 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7053;http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/appsettings.Development.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/appsettings.Development.json new file mode 100644 index 00000000..17f5221f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/appsettings.Development.json @@ -0,0 +1,25 @@ +{ + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:5001" // Set your fixed port here + } + } + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "DatabaseConnectionStrings": { + "IdentityConnectionString": "Data Source=localhost;Initial Catalog=PlanningBookIdentity;User Id=;Password=;TrustServerCertificate=True;" + }, + "Jwt": { + "Secret": "MlKMklmnJV7c24A1gAtcrlD5AY0hPShCaqZym3aYBUM", + "Issuer": "http://localhost:5001", + "Audience": "http://localhost:5001", + "ExpirationInMinutes": 180, + "RefreshTokenExpirationInDays": 7 + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/appsettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/ChangePasswordClientAccountCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/ChangePasswordClientAccountCommand.cs new file mode 100644 index 00000000..86d050ce --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/ChangePasswordClientAccountCommand.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Identity.Infrastructure.Entities; + +namespace PlanningBook.Identity.Application.ClientAccounts.Commands +{ + #region Command Model + public sealed class ChangePasswordClientAccountCommand : ICommand> + { + public Guid? UserId { get; set; } + public string OldPassword { get; set; } + public string NewPassword { get; set; } + + public ChangePasswordClientAccountCommand(string oldPassword, string newPassword) + { + OldPassword = oldPassword; + NewPassword = newPassword; + } + + public ValidationResult GetValidationResult() + { + var invalid = string.IsNullOrWhiteSpace(OldPassword) || + string.IsNullOrWhiteSpace(NewPassword) || + UserId == Guid.Empty || + UserId == null; + + if (invalid) + return ValidationResult.Failure(null, new List() { "Changes Password Failed!" }); + + return ValidationResult.Success(); + } + } + #endregion Command Model + + #region Command Handler + public sealed class ChangePasswordClientAccountCommandHandler(UserManager _userManager) + : ICommandHandler> + { + public async Task> HandleAsync(ChangePasswordClientAccountCommand command, CancellationToken cancellationToken = default) + { + if (command == null || !command.GetValidationResult().IsValid) + // TODO: Log Error + return CommandResult.Failure(null, null); + + var userExisted = await _userManager.Users + .FirstOrDefaultAsync(account => account.Id == command.UserId && + account.IsActive && + !account.IsDeleted, cancellationToken); + + if (userExisted == null) + return CommandResult.Failure(null, null); + + var result = await _userManager.ChangePasswordAsync(userExisted, command.OldPassword, command.NewPassword); + + return CommandResult.Success(result.Succeeded); + } + } + #endregion Command Handler +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/CommandResults/SignInClientAccountCommandResult.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/CommandResults/SignInClientAccountCommandResult.cs new file mode 100644 index 00000000..c3e8e9c8 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/CommandResults/SignInClientAccountCommandResult.cs @@ -0,0 +1,9 @@ +namespace PlanningBook.Identity.Application.ClientAccounts.Commands.CommandResults +{ + public class SignInClientAccountCommandResult + { + public Guid UserId { get; set; } + public string Token { get; set; } + public string RefreshToken { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignInClientAccountCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignInClientAccountCommand.cs new file mode 100644 index 00000000..0d206fdc --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignInClientAccountCommand.cs @@ -0,0 +1,102 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Identity.Application.ClientAccounts.Commands.CommandResults; +using PlanningBook.Identity.Application.Helpers.Interfaces; +using PlanningBook.Identity.Application.Providers.Interfaces; +using PlanningBook.Identity.Infrastructure; +using PlanningBook.Identity.Infrastructure.Entities; +using PlanningBook.Repository.EF; + +namespace PlanningBook.Identity.Application.ClientAccounts.Commands +{ + #region Command Model + public sealed class SignInClientAccountCommand : ICommand> + { + public string UserName { get; set; } + public string Password { get; set; } + + public SignInClientAccountCommand(string username, string password) + { + UserName = username; + Password = password; + } + + public ValidationResult GetValidationResult() + { + var invalid = string.IsNullOrWhiteSpace(UserName) || string.IsNullOrWhiteSpace(Password); + if (invalid) + return ValidationResult.Failure(null, null); + + return ValidationResult.Success(); + } + } + #endregion Command Model + + #region Command Handler + public sealed class SignInClientAccountCommandHandler( + UserManager _userManager, + SignInManager _signInManager, + IConfiguration _configuration, + IEFClassRepository _accountTokenRepository, + IPasswordHasher _passwordHasher, + ITokenProvider _tokenProvider) + : ICommandHandler> + { + public async Task> HandleAsync(SignInClientAccountCommand command, CancellationToken cancellationToken = default) + { + if (command == null || !command.GetValidationResult().IsValid) + return CommandResult.Failure(null, null); + + var accountExisted = await _userManager.Users + .FirstOrDefaultAsync(account => account.NormalizedUserName.Equals(command.UserName.ToUpper())); + + // TODO: Seperate error & log + if (accountExisted == null || !accountExisted.IsActive || accountExisted.IsDeleted) + return CommandResult.Failure(null, null); + + var validPassword = _passwordHasher.Verify(command.Password, accountExisted.PasswordHash); + if (!validPassword) + return CommandResult.Failure(null, null); + + //await _signInManager.SignInAsync(accountExisted, true); + //var signInResult = await _signInManager.PasswordSignInAsync(command.UserName, accountExisted.PasswordHash, true, false); + //if (!signInResult) + // return CommandResult.Failure(null, null); + + var token = _tokenProvider.GenerateToken(accountExisted); + var refreshToken = _tokenProvider.GenerateRefreshToken(); + + // TODO: Should not delete => Inplement revoke token & refesh token later + //await _accountTokenRepository.HardDeleteAsync(x => x.AccountId == accountExisted.Id, cancellationToken); + + var configExpirationDays = _configuration.GetValue("Jwt:RefreshTokenExpirationInDays", 7); + // TODO: Need To Fix later + //var newToken = new AccountToken() + //{ + // Name = $"{accountExisted.Id}-{DateTime.UtcNow.ToString()}", + // AccountId = accountExisted.Id, + // // TODO: Need some constant for LoginProvider + // LoginProvider = "InternalIdentitySystem", + // Token = token, + // RefreshToken = refreshToken, + // RefreshTokenExpirationDate = DateTime.UtcNow.AddDays(configExpirationDays), + // IsRevoked = false + //}; + //await _accountTokenRepository.AddAsync(newToken, cancellationToken); + + //// TODO: Should re-implement unitOfWork for case save change many different tables + //await _accountTokenRepository.SaveChangeAsync(); + + return CommandResult.Success(new SignInClientAccountCommandResult() + { + UserId = accountExisted.Id, + Token = token, + RefreshToken = refreshToken + }); + } + } + #endregion Command Handler +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignOutClientAccountCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignOutClientAccountCommand.cs new file mode 100644 index 00000000..882cb5c2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignOutClientAccountCommand.cs @@ -0,0 +1,70 @@ +using Microsoft.AspNetCore.Identity; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Identity.Infrastructure; +using PlanningBook.Identity.Infrastructure.Entities; +using PlanningBook.Repository.EF; + +namespace PlanningBook.Identity.Application.ClientAccounts.Commands +{ + // TODO: Implement + #region Command Model + public sealed class SignOutClientAccountCommand : ICommand> + { + public Guid? AccountId { get; set; } + public string? Token { get; set; } + public ValidationResult GetValidationResult() + { + var invalid = AccountId == Guid.Empty || string.IsNullOrWhiteSpace(Token); + if (invalid) + return ValidationResult.Failure(null, null); + + return ValidationResult.Success(); + } + } + #endregion Command Model + + #region Command Handler + // TODO: Create token & refresh token revoke Service + public sealed class SignOutClientAccountCommandHandler( + SignInManager _signInManager, + UserManager _accountManager, + IEFClassRepository _accountTokenRepository, + IEFRepository _revokedTokenRepository, + PBIdentityDbContext _pBIdentityDbContext) + : ICommandHandler> + { + public async Task> HandleAsync(SignOutClientAccountCommand command, CancellationToken cancellationToken = default) + { + if (!command.GetValidationResult().IsValid) + return CommandResult.Failure(null, null); + + var tokenExisted = await _accountTokenRepository + .GetFirstAsync(x => x.AccountId == command.AccountId && + x.Token == command.Token, cancellationToken); + + if (tokenExisted == null || tokenExisted.IsRevoked) + { + // TODO: Log error + return CommandResult.Success(true); + } + + tokenExisted.IsRevoked = true; + await _accountTokenRepository.UpdateAsync(tokenExisted, cancellationToken); + //await _accountTokenRepository.SaveChangeAsync(); + + var revokedToken = new RevokedToken() + { + Id = tokenExisted.Token + }; + await _revokedTokenRepository.AddAsync(revokedToken, cancellationToken); + + await _signInManager.SignOutAsync(); + + await _pBIdentityDbContext.SaveChangesAsync(cancellationToken); + + return CommandResult.Success(true); + } + } + #endregion Comamnd Handler +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignUpClientAccountCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignUpClientAccountCommand.cs new file mode 100644 index 00000000..934fedbc --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/ClientAccounts/Commands/SignUpClientAccountCommand.cs @@ -0,0 +1,149 @@ +using Microsoft.AspNetCore.Identity; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Identity.Infrastructure.Entities; +using PlanningBook.Domain; +using Microsoft.EntityFrameworkCore; +using PlanningBook.Identity.Application.Helpers.Interfaces; +using System.Net.Http; +using System.Text.Json; +using System.Text; + +namespace PlanningBook.Identity.Application.Accounts.Commands +{ + #region Command Model + public sealed class SignUpClientAccountCommand : ICommand> + { + public string Username { get; set; } + public string Password { get; set; } // TO DO: Should not plain text from FE -> BE + public string? Email { get; set; } + public string? PhoneNumber { get; set; } + public SignUpClientAccountCommand(string username, string password, string? email, string? phoneNumber) + { + Username = username; + Password = password; + Email = email; + PhoneNumber = phoneNumber; + } + + public ValidationResult GetValidationResult() + { + var invalid = string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password); + if (invalid) + return ValidationResult.Failure(null, new List() + { + "Register Account Failed: Invalid request!" + }); + + return ValidationResult.Success(); + } + } + #endregion Command Model + + #region Command Handler + public sealed class SignUpClientAccountCommandHandler( + UserManager _accountManager, + IPasswordHasher _passwordHasher, + IHttpClientFactory _httpClientFactory + ) + : ICommandHandler> + { + public async Task> HandleAsync(SignUpClientAccountCommand command, CancellationToken cancellationToken = default) + { + if (command == null || !command.GetValidationResult().IsValid) + { + // TODO: Log Error + // TODO: Handle throw error with error message + return CommandResult.Failure(null, null); + } + + //var accountExisted = await _accountManager.FindByEmailAsync(command.Email); + //var accountExisted = await _accountManager.Users + // .FirstOrDefaultAsync(x => x.UserName == command.Username && + // x.); + + var tempEmail = command?.Email ?? $"{command.Username}@mail.com"; + if (!string.IsNullOrWhiteSpace(tempEmail)) + { + var isEmailUsed = await _accountManager.Users + .AnyAsync(account => account.Email == tempEmail && + account.IsActive && + !account.IsDeleted, cancellationToken); + if (isEmailUsed) + { + // TODO: Log Error & Return Error + return CommandResult.Failure(null, null); + } + } + + if (!string.IsNullOrWhiteSpace(command.PhoneNumber)) + { + var isPhoneUsed = await _accountManager.Users + .AnyAsync(account => account.PhoneNumber == command.PhoneNumber && + account.IsActive && + !account.IsDeleted, cancellationToken); + + if (isPhoneUsed) + { + // TODO: Log Error & Return Error + return CommandResult.Failure(null, null); + } + } + + var accountExisted = await _accountManager.Users + .AnyAsync(account => account.UserName == command.Username + && account.Email == tempEmail + && account.PhoneNumber == command.PhoneNumber + && account.IsActive + && !account.IsDeleted, cancellationToken); + + if (accountExisted) + { + // TODO: Log Error + //TODO: Improve for flow SSO (with Link account) + // Example User craete an account username/password with email A + // After that the login by Gmail with same email A + // => Ask to login by username/passwork and link Gmail in account setting + return CommandResult.Failure(null, null); + } + + var passwordHash = _passwordHasher.Hash(command.Password); + var account = new Account() + { + UserName = command.Username, + NormalizedUserName = command.Username.ToUpper(), + Email = tempEmail, + NormalizedEmail = tempEmail.ToUpper(), + PhoneNumber = command?.PhoneNumber ?? null, + PasswordHash = passwordHash, + IsDeleted = false, + IsActive = true + }; + + var result = await _accountManager.CreateAsync(account); + if (!result.Succeeded) + { + return CommandResult.Failure(null, result.Errors.ToString()); + } + + // TODO: Send Confirmed email & sms + // TODO: Send to Person Service API to create Person record + account.PasswordHash = passwordHash; + await _accountManager.UpdateAsync(account); + + #region Create Person + //TODO: Should use bus to handler + var client = _httpClientFactory.CreateClient("Person"); + var jsonContent = new StringContent(JsonSerializer.Serialize(command), Encoding.UTF8, "application/json"); + var response = await client.PostAsync("ExposurePerson/Create", jsonContent); + + if(response.IsSuccessStatusCode) + { + var test = 1; + } + #endregion Create Person + + return CommandResult.Success(account.Id); + } + } + #endregion Command Handler +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Helpers/Interfaces/IPasswordHasher.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Helpers/Interfaces/IPasswordHasher.cs new file mode 100644 index 00000000..549e6b46 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Helpers/Interfaces/IPasswordHasher.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Identity.Application.Helpers.Interfaces +{ + public interface IPasswordHasher + { + string Hash(string passwordPlainText); + bool Verify(string passwordPlainText, string passwordHash); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Helpers/PasswordHasher.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Helpers/PasswordHasher.cs new file mode 100644 index 00000000..677d7574 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Helpers/PasswordHasher.cs @@ -0,0 +1,59 @@ +using PlanningBook.Identity.Application.Helpers.Interfaces; +using System.Security.Cryptography; +using System.Text; + +namespace PlanningBook.Identity.Application.Helpers +{ + public sealed class PasswordHasher : IPasswordHasher + { + private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + private const int saltSize = 16; + private const int hashSize = 32; + private const int iterations = 500000; + private static readonly HashAlgorithmName algorithm = HashAlgorithmName.SHA512; + + public string Hash(string passwordPlainText) + { + byte[] salt = GenerateRandomSalt(saltSize); + byte[] hash = Rfc2898DeriveBytes.Pbkdf2(passwordPlainText, salt, iterations, algorithm, hashSize); + + return $"{Convert.ToHexString(hash)}-{Convert.ToHexString(salt)}"; + } + + public bool Verify(string passwordPlainText, string passwordHash) + { + string[] parts = passwordHash.Split('-'); + byte[] hash = Convert.FromHexString(parts[0]); + byte[] salt = Convert.FromHexString(parts[1]); + + byte[] inputHash = Rfc2898DeriveBytes.Pbkdf2(passwordPlainText, salt, iterations, algorithm, hashSize); + + return CryptographicOperations.FixedTimeEquals(hash, inputHash); + } + + #region Private methods + private byte[] GenerateRandomSalt(int saltSize) + { + var rawSalt = new StringBuilder(saltSize); + byte[] randomBytes = new byte[1]; + + using (var rng = RandomNumberGenerator.Create()) + { + while (rawSalt.Length < saltSize) + { + rng.GetBytes(randomBytes); + + // Get random index from chars + int index = randomBytes[0] % chars.Length; + + rawSalt.Append(chars[index]); + } + } + + byte[] salt = Encoding.UTF8.GetBytes(rawSalt.ToString()); + + return salt; + } + #endregion Private methods + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/PlanningBook.Identity.Application.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/PlanningBook.Identity.Application.csproj new file mode 100644 index 00000000..33fbe4d3 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/PlanningBook.Identity.Application.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Providers/Interfaces/ITokenProvider.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Providers/Interfaces/ITokenProvider.cs new file mode 100644 index 00000000..b4606ae1 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Providers/Interfaces/ITokenProvider.cs @@ -0,0 +1,10 @@ +using PlanningBook.Identity.Infrastructure.Entities; + +namespace PlanningBook.Identity.Application.Providers.Interfaces +{ + public interface ITokenProvider + { + string GenerateToken(Account account); + string GenerateRefreshToken(); + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Providers/TokenProvider.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Providers/TokenProvider.cs new file mode 100644 index 00000000..4ed06bbd --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Application/Providers/TokenProvider.cs @@ -0,0 +1,54 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; +using PlanningBook.Identity.Application.Providers.Interfaces; +using PlanningBook.Identity.Infrastructure.Entities; + +namespace PlanningBook.Identity.Application.Providers +{ + public sealed class TokenProvider : ITokenProvider + { + private readonly IConfiguration _configuration; + + public TokenProvider(IConfiguration configuration) + { + _configuration = configuration; + } + + public string GenerateRefreshToken() + { + var randomNumber = new byte[32]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomNumber); + return Convert.ToBase64String(randomNumber); + } + } + + public string GenerateToken(Account account) + { + string secretKey = _configuration.GetValue("Jwt:Secret"); + string issuer = _configuration.GetValue("Jwt:Issuer"); + string audience = _configuration.GetValue("Jwt:Audience"); + int expirationInMinutes = _configuration.GetValue("Jwt:ExpirationInMinutes"); + + if (string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(issuer) || string.IsNullOrWhiteSpace(audience)) + return string.Empty; + + var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); + var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); + + var claims = new List() { + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim(ClaimTypes.NameIdentifier, account.Id.ToString()) + }; + + var token = new JwtSecurityToken(issuer, audience, claims, expires: DateTime.UtcNow.AddMinutes(1800), signingCredentials: credentials); + + return new JwtSecurityTokenHandler().WriteToken(token); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Constants/AccountRoleConstants.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Constants/AccountRoleConstants.cs new file mode 100644 index 00000000..68acbe39 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Constants/AccountRoleConstants.cs @@ -0,0 +1,51 @@ +namespace PlanningBook.Identity.Infrastructure.Constants +{ + public static class AccountRoleConstants + { + #region Role Names + public const string SuperAdminRoleName = "Super Admin"; + public const string AdminRoleName = "Admin"; + public const string UserRoleName = "User"; + #endregion Role Names + + #region Role Ids + public static readonly Guid SuperAdminRoleId = new Guid("5dae3b58-d3bb-4a9d-8efb-71518621bfc6"); + public static readonly Guid AdminRoleId = new Guid("dd0d2565-b017-4d96-9abf-c981366b6d62"); + public static readonly Guid UserRoleId = new Guid("f7846fe7-3c67-4da4-9185-843143948afa"); + #endregion Role Ids + + #region Helper Methods + /// + /// Get Role Id by role name + /// + /// + /// Role Id as Guid + public static Guid? GetRoleId(string roleName) + { + return roleName switch + { + SuperAdminRoleName => SuperAdminRoleId, + AdminRoleName => AdminRoleId, + UserRoleName => UserRoleId, + _ => null + }; + } + + /// + /// Get Role Name by role Id + /// + /// + /// Role Name as string + public static string? GetRoleName(Guid roleId) + { + return roleId switch + { + _ when roleId == SuperAdminRoleId => SuperAdminRoleName, + _ when roleId == AdminRoleId => AdminRoleName, + _ when roleId == UserRoleId => UserRoleName, + _ => null + }; + } + #endregion Helper Methods + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Account.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Account.cs new file mode 100644 index 00000000..80ebcac2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Account.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Identity; +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class Account : IdentityUser, IEntityBase, IFullAudited, + ISoftDeleted, IActiveEntity + { + public DateTime? CreatedDate { get; set; } + public DateTime? UpdatedDate { get; set; } + public Guid? CreatedBy { get; set; } + //public virtual Account? CreateByAccount { get; set; } + public Guid? UpdatedBy { get; set; } + //public virtual Account? UpdatedByAccount { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletedAt { get; set; } + public bool IsActive { get; set; } + + // One to many + public ICollection Claims { get; set; } + public ICollection Logins { get; set; } + public ICollection Roles { get; set; } + public ICollection Tokens { get; set; } + public ICollection Persons { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountClaim.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountClaim.cs new file mode 100644 index 00000000..b3884aec --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountClaim.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Identity; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class AccountClaim : IdentityUserClaim + { + public Guid AccountId { get; set; } + public Account Account { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountLogin.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountLogin.cs new file mode 100644 index 00000000..5db31f5c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountLogin.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Identity; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class AccountLogin : IdentityUserLogin + { + public Guid AccountId { get; set; } + public Account Account { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountPerson.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountPerson.cs new file mode 100644 index 00000000..79120edf --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountPerson.cs @@ -0,0 +1,13 @@ +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class AccountPerson : IDateAudited + { + public Guid AccountId { get; set; } + public Account Account { get; set; } + public Guid PersonId { get; set; } + public DateTime? CreatedDate { get; set; } + public DateTime? UpdatedDate { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountRole.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountRole.cs new file mode 100644 index 00000000..1178f465 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountRole.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Identity; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class AccountRole : IdentityUserRole + { + public Guid AccountId { get; set; } + public Account Account { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountToken.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountToken.cs new file mode 100644 index 00000000..59e3f6ba --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/AccountToken.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Identity; +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class AccountToken : IdentityUserToken, IDateAudited + { + public Guid AccountId { get; set; } + public Account Account { get; set; } + public string Token { get; set; } + public string RefreshToken { get; set; } + // TODO: Need Cron Job to check and make IsRevoked Refresh token + public DateTime RefreshTokenExpirationDate { get; set; } + /// + /// It use for force revoke: Both Token & Refresh Token is invalid now + /// + public bool IsRevoked { get; set; } + public DateTime? CreatedDate { get; set; } + public DateTime? UpdatedDate { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountClaimConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountClaimConfiguration.cs new file mode 100644 index 00000000..4c219a27 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountClaimConfiguration.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class AccountClaimConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.UserId) + .HasColumnName(nameof(AccountClaim.AccountId)); + builder.Ignore(p => p.AccountId); + + builder.HasOne(ac => ac.Account) + .WithMany(a => a.Claims) + .HasForeignKey(ac => ac.AccountId) + .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountConfiguration.cs new file mode 100644 index 00000000..84dbd333 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountConfiguration.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class AccountConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + //builder.HasMany(a => a.Claims) + // .WithOne(ac => ac.Account) + // .HasForeignKey(ac => ac.AccountId) + // .OnDelete(DeleteBehavior.Restrict); + + //builder.HasMany(a => a.Logins) + // .WithOne(l => l.Account) + // .HasForeignKey(l => l.AccountId) + // .OnDelete(DeleteBehavior.Restrict); + + //builder.HasMany(a => a.Roles) + // .WithOne(r => r.Account) + // .HasForeignKey(r => r.AccountId) + // .OnDelete(DeleteBehavior.Restrict); + + //builder.HasMany(a => a.Tokens) + // .WithOne(t => t.Account) + // .HasForeignKey(t => t.AccountId) + // .OnDelete(DeleteBehavior.Restrict); + + //builder.HasMany(a => a.Persons) + // .WithOne(p => p.Account) + // .HasForeignKey(p => p.AccountId) + // .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountLoginConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountLoginConfiguration.cs new file mode 100644 index 00000000..084b28df --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountLoginConfiguration.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class AccountLoginConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.UserId) + .HasColumnName(nameof(AccountLogin.AccountId)); + builder.Ignore(p => p.AccountId); + + builder.HasOne(l => l.Account) + .WithMany(a => a.Logins) + .HasForeignKey(l => l.AccountId) + .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountPersonConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountPersonConfiguration.cs new file mode 100644 index 00000000..6c7847b7 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountPersonConfiguration.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class AccountPersonConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.HasKey(ap => new { ap.AccountId, ap.PersonId }); + + builder.HasOne(ap => ap.Account) + .WithMany(a => a.Persons) + .HasForeignKey(ap => ap.AccountId) + .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountRoleConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountRoleConfiguration.cs new file mode 100644 index 00000000..a08032f5 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountRoleConfiguration.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class AccountRoleConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.UserId) + .HasColumnName(nameof(AccountRole.AccountId)); + builder.Ignore(p => p.AccountId); + + builder.HasOne(r => r.Account) + .WithMany(a => a.Roles) + .HasForeignKey(r => r.AccountId) + .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountTokenConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountTokenConfiguration.cs new file mode 100644 index 00000000..00de9466 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/AccountTokenConfiguration.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class AccountTokenConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.UserId) + .HasColumnName(nameof(AccountToken.AccountId)); + builder.Ignore(p => p.AccountId); + + builder.Property(p => p.Value) + .HasColumnName(nameof(AccountToken.Token)); + builder.Ignore(p => p.Token); + + builder.HasOne(t => t.Account) + .WithMany(a => a.Tokens) + .HasForeignKey(t => t.AccountId) + .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RevokedTokenConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RevokedTokenConfiguration.cs new file mode 100644 index 00000000..1f14f456 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RevokedTokenConfiguration.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class RevokedTokenConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RoleClaimConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RoleClaimConfiguration.cs new file mode 100644 index 00000000..725a8620 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RoleClaimConfiguration.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class RoleClaimConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RoleConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RoleConfiguration.cs new file mode 100644 index 00000000..59612a01 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Configurations/RoleConfiguration.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; +using PlanningBook.Identity.Infrastructure.Enums; + +namespace PlanningBook.Identity.Infrastructure.Entities.Configurations +{ + public class RoleConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + #region Seed Data + builder.HasData( + new Role() + { + Id = new Guid("4155f55f-b7e7-4529-865b-63f2ea7865fa"), + RoleType = RoleType.SysAdmin, + AppliedEntity = RoleAppliedEntity.Account, + Name = $"a_{RoleType.SysAdmin.ToString()}", + NormalizedName = $"a_{RoleType.SysAdmin.ToString().ToLower()}", + }, + new Role() + { + Id = new Guid("f8183db5-a09e-41d8-939b-c188a6247651"), + RoleType = RoleType.User, + AppliedEntity = RoleAppliedEntity.Account, + Name = $"a_{RoleType.User.ToString()}", + NormalizedName = $"a_{RoleType.User.ToString().ToLower()}" + }, + new Role() + { + Id = new Guid("a5a8bd75-c04b-4ad8-b1b7-b1db912ae8ef"), + RoleType = RoleType.Staff, + AppliedEntity = RoleAppliedEntity.Person, + Name = $"p_{RoleType.Staff.ToString()}", + NormalizedName = $"p_{RoleType.Staff.ToString().ToLower()}" + }, + new Role() + { + Id = new Guid("281c5aa9-f0bd-42bc-9c9f-060895fb4187"), + RoleType = RoleType.EndUser, + AppliedEntity = RoleAppliedEntity.Person, + Name = $"p_{RoleType.EndUser.ToString()}", + NormalizedName = $"p_{RoleType.EndUser.ToString().ToLower()}" + }); + #endregion Seed Data + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/RevokedToken.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/RevokedToken.cs new file mode 100644 index 00000000..e1900991 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/RevokedToken.cs @@ -0,0 +1,13 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class RevokedToken : EntityBase, IDateAudited, IAuthorAudited + { + public DateTime? CreatedDate { get; set; } + public DateTime? UpdatedDate { get; set; } + public Guid? CreatedBy { get; set; } + public Guid? UpdatedBy { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Role.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Role.cs new file mode 100644 index 00000000..920fa16b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/Role.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Identity; +using PlanningBook.Identity.Infrastructure.Enums; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class Role : IdentityRole + { + public RoleType RoleType { get; set; } + public RoleAppliedEntity AppliedEntity { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/RoleClaim.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/RoleClaim.cs new file mode 100644 index 00000000..a544781a --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Entities/RoleClaim.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Identity; + +namespace PlanningBook.Identity.Infrastructure.Entities +{ + public class RoleClaim : IdentityRoleClaim + { + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Enums/RoleAppliedEntity.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Enums/RoleAppliedEntity.cs new file mode 100644 index 00000000..c89dd633 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Enums/RoleAppliedEntity.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Identity.Infrastructure.Enums +{ + public enum RoleAppliedEntity + { + Account, + Person + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Enums/RoleType.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Enums/RoleType.cs new file mode 100644 index 00000000..e76917b2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Enums/RoleType.cs @@ -0,0 +1,16 @@ +namespace PlanningBook.Identity.Infrastructure.Enums +{ + // Any changes should sync with PersonRole & AccountRole in our Frameworks->PlanningBook.Enums + public enum RoleType + { + #region Account Roles + SysAdmin = 1, + User = 1001, + #endregion Account Roles + + #region Person Roles + Staff = 3000, + EndUser = 4001 + #endregion Person Roles + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240815094928_InititalIdentityDatabase.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240815094928_InititalIdentityDatabase.Designer.cs new file mode 100644 index 00000000..a7ab15af --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240815094928_InititalIdentityDatabase.Designer.cs @@ -0,0 +1,297 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Identity.Infrastructure; + +#nullable disable + +namespace PlanningBook.Identity.Infrastructure.Migrations +{ + [DbContext(typeof(PBIdentityDbContext))] + [Migration("20240815094928_InititalIdentityDatabase")] + partial class InititalIdentityDatabase + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedById") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UpdatedById") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("Accounts", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccountClaims", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AccountLogins", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AccountRoles", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AccountTokens", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RoleType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("Roles", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountClaim", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountLogin", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountRole", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountToken", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RoleClaim", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240815094928_InititalIdentityDatabase.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240815094928_InititalIdentityDatabase.cs new file mode 100644 index 00000000..2961e65f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240815094928_InititalIdentityDatabase.cs @@ -0,0 +1,229 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Identity.Infrastructure.Migrations +{ + /// + public partial class InititalIdentityDatabase : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Accounts", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: true), + UpdatedDate = table.Column(type: "datetime2", nullable: true), + CreatedById = table.Column(type: "uniqueidentifier", nullable: true), + UpdatedById = table.Column(type: "uniqueidentifier", nullable: true), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Accounts", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Roles", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + RoleType = table.Column(type: "int", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Roles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AccountClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + AccountId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AccountClaims", x => x.Id); + table.ForeignKey( + name: "FK_AccountClaims_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AccountLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + AccountId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AccountLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AccountLogins_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AccountTokens", + columns: table => new + { + AccountId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AccountTokens", x => new { x.AccountId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AccountTokens_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AccountRoles", + columns: table => new + { + AccountId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AccountRoles", x => new { x.AccountId, x.RoleId }); + table.ForeignKey( + name: "FK_AccountRoles_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AccountRoles_Roles_RoleId", + column: x => x.RoleId, + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "RoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_RoleClaims_Roles_RoleId", + column: x => x.RoleId, + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AccountClaims_AccountId", + table: "AccountClaims", + column: "AccountId"); + + migrationBuilder.CreateIndex( + name: "IX_AccountLogins_AccountId", + table: "AccountLogins", + column: "AccountId"); + + migrationBuilder.CreateIndex( + name: "IX_AccountRoles_RoleId", + table: "AccountRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "Accounts", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "Accounts", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_RoleClaims_RoleId", + table: "RoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "Roles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AccountClaims"); + + migrationBuilder.DropTable( + name: "AccountLogins"); + + migrationBuilder.DropTable( + name: "AccountRoles"); + + migrationBuilder.DropTable( + name: "AccountTokens"); + + migrationBuilder.DropTable( + name: "RoleClaims"); + + migrationBuilder.DropTable( + name: "Accounts"); + + migrationBuilder.DropTable( + name: "Roles"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240920060918_Update_Database_SChema.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240920060918_Update_Database_SChema.Designer.cs new file mode 100644 index 00000000..cc77d3b2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240920060918_Update_Database_SChema.Designer.cs @@ -0,0 +1,493 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Identity.Infrastructure; + +#nullable disable + +namespace PlanningBook.Identity.Infrastructure.Migrations +{ + [DbContext(typeof(PBIdentityDbContext))] + [Migration("20240920060918_Update_Database_SChema")] + partial class Update_Database_SChema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("Accounts", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("UserId"); + + b.ToTable("AccountClaims", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("AccountId"); + + b.HasIndex("UserId"); + + b.ToTable("AccountLogins", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountPerson", b => + { + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("PersonId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("AccountId", "PersonId"); + + b.ToTable("AccountPersons", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("AccountId"); + + b.HasIndex("RoleId"); + + b.ToTable("AccountRoles", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("IsRevoked") + .HasColumnType("bit"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RefreshTokenExpirationDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("Value") + .HasColumnType("nvarchar(max)") + .HasColumnName("Token"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.HasIndex("AccountId"); + + b.ToTable("AccountTokens", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RevokedToken", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("RevokedTokens", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppliedEntity") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RoleType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("Roles", (string)null); + + b.HasData( + new + { + Id = new Guid("4155f55f-b7e7-4529-865b-63f2ea7865fa"), + AppliedEntity = 0, + Name = "a_SysAdmin", + NormalizedName = "a_sysadmin", + RoleType = 1 + }, + new + { + Id = new Guid("f8183db5-a09e-41d8-939b-c188a6247651"), + AppliedEntity = 0, + Name = "a_User", + NormalizedName = "a_user", + RoleType = 1001 + }, + new + { + Id = new Guid("a5a8bd75-c04b-4ad8-b1b7-b1db912ae8ef"), + AppliedEntity = 1, + Name = "p_Staff", + NormalizedName = "p_staff", + RoleType = 3000 + }, + new + { + Id = new Guid("281c5aa9-f0bd-42bc-9c9f-060895fb4187"), + AppliedEntity = 1, + Name = "p_EndUser", + NormalizedName = "p_enduser", + RoleType = 4001 + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountClaim", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Claims") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountLogin", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Logins") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountPerson", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Persons") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountRole", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Roles") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountToken", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Tokens") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RoleClaim", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Account", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("Persons"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240920060918_Update_Database_SChema.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240920060918_Update_Database_SChema.cs new file mode 100644 index 00000000..09d909e9 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/20240920060918_Update_Database_SChema.cs @@ -0,0 +1,347 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace PlanningBook.Identity.Infrastructure.Migrations +{ + /// + public partial class Update_Database_SChema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Value", + table: "AccountTokens", + newName: "Token"); + + migrationBuilder.RenameColumn( + name: "UpdatedById", + table: "Accounts", + newName: "UpdatedBy"); + + migrationBuilder.RenameColumn( + name: "CreatedById", + table: "Accounts", + newName: "CreatedBy"); + + migrationBuilder.AddColumn( + name: "AppliedEntity", + table: "Roles", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "AccountId1", + table: "AccountTokens", + type: "uniqueidentifier", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "CreatedDate", + table: "AccountTokens", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsRevoked", + table: "AccountTokens", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "RefreshToken", + table: "AccountTokens", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "RefreshTokenExpirationDate", + table: "AccountTokens", + type: "datetime2", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "UpdatedDate", + table: "AccountTokens", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletedAt", + table: "Accounts", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsActive", + table: "Accounts", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "Accounts", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "AccountId1", + table: "AccountRoles", + type: "uniqueidentifier", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "AccountId1", + table: "AccountLogins", + type: "uniqueidentifier", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "AccountId1", + table: "AccountClaims", + type: "uniqueidentifier", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.CreateTable( + name: "AccountPersons", + columns: table => new + { + AccountId = table.Column(type: "uniqueidentifier", nullable: false), + PersonId = table.Column(type: "uniqueidentifier", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: true), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AccountPersons", x => new { x.AccountId, x.PersonId }); + table.ForeignKey( + name: "FK_AccountPersons_Accounts_AccountId", + column: x => x.AccountId, + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "RevokedTokens", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: true), + UpdatedDate = table.Column(type: "datetime2", nullable: true), + CreatedBy = table.Column(type: "uniqueidentifier", nullable: true), + UpdatedBy = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RevokedTokens", x => x.Id); + }); + + migrationBuilder.InsertData( + table: "Roles", + columns: new[] { "Id", "AppliedEntity", "ConcurrencyStamp", "Name", "NormalizedName", "RoleType" }, + values: new object[,] + { + { new Guid("281c5aa9-f0bd-42bc-9c9f-060895fb4187"), 1, null, "p_EndUser", "p_enduser", 4001 }, + { new Guid("4155f55f-b7e7-4529-865b-63f2ea7865fa"), 0, null, "a_SysAdmin", "a_sysadmin", 1 }, + { new Guid("a5a8bd75-c04b-4ad8-b1b7-b1db912ae8ef"), 1, null, "p_Staff", "p_staff", 3000 }, + { new Guid("f8183db5-a09e-41d8-939b-c188a6247651"), 0, null, "a_User", "a_user", 1001 } + }); + + migrationBuilder.CreateIndex( + name: "IX_AccountTokens_AccountId1", + table: "AccountTokens", + column: "AccountId1"); + + migrationBuilder.CreateIndex( + name: "IX_AccountRoles_AccountId1", + table: "AccountRoles", + column: "AccountId1"); + + migrationBuilder.CreateIndex( + name: "IX_AccountLogins_AccountId1", + table: "AccountLogins", + column: "AccountId1"); + + migrationBuilder.CreateIndex( + name: "IX_AccountClaims_AccountId1", + table: "AccountClaims", + column: "AccountId1"); + + migrationBuilder.AddForeignKey( + name: "FK_AccountClaims_Accounts_AccountId1", + table: "AccountClaims", + column: "AccountId1", + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AccountLogins_Accounts_AccountId1", + table: "AccountLogins", + column: "AccountId1", + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AccountRoles_Accounts_AccountId1", + table: "AccountRoles", + column: "AccountId1", + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AccountTokens_Accounts_AccountId1", + table: "AccountTokens", + column: "AccountId1", + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AccountClaims_Accounts_AccountId1", + table: "AccountClaims"); + + migrationBuilder.DropForeignKey( + name: "FK_AccountLogins_Accounts_AccountId1", + table: "AccountLogins"); + + migrationBuilder.DropForeignKey( + name: "FK_AccountRoles_Accounts_AccountId1", + table: "AccountRoles"); + + migrationBuilder.DropForeignKey( + name: "FK_AccountTokens_Accounts_AccountId1", + table: "AccountTokens"); + + migrationBuilder.DropTable( + name: "AccountPersons"); + + migrationBuilder.DropTable( + name: "RevokedTokens"); + + migrationBuilder.DropIndex( + name: "IX_AccountTokens_AccountId1", + table: "AccountTokens"); + + migrationBuilder.DropIndex( + name: "IX_AccountRoles_AccountId1", + table: "AccountRoles"); + + migrationBuilder.DropIndex( + name: "IX_AccountLogins_AccountId1", + table: "AccountLogins"); + + migrationBuilder.DropIndex( + name: "IX_AccountClaims_AccountId1", + table: "AccountClaims"); + + migrationBuilder.DeleteData( + table: "Roles", + keyColumn: "Id", + keyValue: new Guid("281c5aa9-f0bd-42bc-9c9f-060895fb4187")); + + migrationBuilder.DeleteData( + table: "Roles", + keyColumn: "Id", + keyValue: new Guid("4155f55f-b7e7-4529-865b-63f2ea7865fa")); + + migrationBuilder.DeleteData( + table: "Roles", + keyColumn: "Id", + keyValue: new Guid("a5a8bd75-c04b-4ad8-b1b7-b1db912ae8ef")); + + migrationBuilder.DeleteData( + table: "Roles", + keyColumn: "Id", + keyValue: new Guid("f8183db5-a09e-41d8-939b-c188a6247651")); + + migrationBuilder.DropColumn( + name: "AppliedEntity", + table: "Roles"); + + migrationBuilder.DropColumn( + name: "AccountId1", + table: "AccountTokens"); + + migrationBuilder.DropColumn( + name: "CreatedDate", + table: "AccountTokens"); + + migrationBuilder.DropColumn( + name: "IsRevoked", + table: "AccountTokens"); + + migrationBuilder.DropColumn( + name: "RefreshToken", + table: "AccountTokens"); + + migrationBuilder.DropColumn( + name: "RefreshTokenExpirationDate", + table: "AccountTokens"); + + migrationBuilder.DropColumn( + name: "UpdatedDate", + table: "AccountTokens"); + + migrationBuilder.DropColumn( + name: "DeletedAt", + table: "Accounts"); + + migrationBuilder.DropColumn( + name: "IsActive", + table: "Accounts"); + + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "Accounts"); + + migrationBuilder.DropColumn( + name: "AccountId1", + table: "AccountRoles"); + + migrationBuilder.DropColumn( + name: "AccountId1", + table: "AccountLogins"); + + migrationBuilder.DropColumn( + name: "AccountId1", + table: "AccountClaims"); + + migrationBuilder.RenameColumn( + name: "Token", + table: "AccountTokens", + newName: "Value"); + + migrationBuilder.RenameColumn( + name: "UpdatedBy", + table: "Accounts", + newName: "UpdatedById"); + + migrationBuilder.RenameColumn( + name: "CreatedBy", + table: "Accounts", + newName: "CreatedById"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/PBIdentityDbContextModelSnapshot.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/PBIdentityDbContextModelSnapshot.cs new file mode 100644 index 00000000..9af34802 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Migrations/PBIdentityDbContextModelSnapshot.cs @@ -0,0 +1,490 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Identity.Infrastructure; + +#nullable disable + +namespace PlanningBook.Identity.Infrastructure.Migrations +{ + [DbContext(typeof(PBIdentityDbContext))] + partial class PBIdentityDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("Accounts", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("UserId"); + + b.ToTable("AccountClaims", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("AccountId"); + + b.HasIndex("UserId"); + + b.ToTable("AccountLogins", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountPerson", b => + { + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("PersonId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("AccountId", "PersonId"); + + b.ToTable("AccountPersons", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("AccountId"); + + b.HasIndex("RoleId"); + + b.ToTable("AccountRoles", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AccountId"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("IsRevoked") + .HasColumnType("bit"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RefreshTokenExpirationDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("Value") + .HasColumnType("nvarchar(max)") + .HasColumnName("Token"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.HasIndex("AccountId"); + + b.ToTable("AccountTokens", null, t => + { + t.Property("AccountId") + .HasColumnName("AccountId1"); + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RevokedToken", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("RevokedTokens", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppliedEntity") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RoleType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("Roles", (string)null); + + b.HasData( + new + { + Id = new Guid("4155f55f-b7e7-4529-865b-63f2ea7865fa"), + AppliedEntity = 0, + Name = "a_SysAdmin", + NormalizedName = "a_sysadmin", + RoleType = 1 + }, + new + { + Id = new Guid("f8183db5-a09e-41d8-939b-c188a6247651"), + AppliedEntity = 0, + Name = "a_User", + NormalizedName = "a_user", + RoleType = 1001 + }, + new + { + Id = new Guid("a5a8bd75-c04b-4ad8-b1b7-b1db912ae8ef"), + AppliedEntity = 1, + Name = "p_Staff", + NormalizedName = "p_staff", + RoleType = 3000 + }, + new + { + Id = new Guid("281c5aa9-f0bd-42bc-9c9f-060895fb4187"), + AppliedEntity = 1, + Name = "p_EndUser", + NormalizedName = "p_enduser", + RoleType = 4001 + }); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountClaim", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Claims") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountLogin", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Logins") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountPerson", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Persons") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountRole", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Roles") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.AccountToken", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", "Account") + .WithMany("Tokens") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Account", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.RoleClaim", b => + { + b.HasOne("PlanningBook.Identity.Infrastructure.Entities.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PlanningBook.Identity.Infrastructure.Entities.Account", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("Persons"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PBIdentityDbContext.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PBIdentityDbContext.cs new file mode 100644 index 00000000..149885db --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PBIdentityDbContext.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using PlanningBook.Identity.Infrastructure.Entities; +using System.Reflection; + +namespace PlanningBook.Identity.Infrastructure +{ + public class PBIdentityDbContext : IdentityDbContext + { + public PBIdentityDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PBIdentityDbContextFactory.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PBIdentityDbContextFactory.cs new file mode 100644 index 00000000..ca65d794 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PBIdentityDbContextFactory.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using PlanningBook.DBEngine.Constants; + +namespace PlanningBook.Identity.Infrastructure +{ + public class PBIdentityDbContextFactory : IDesignTimeDbContextFactory + { + public PBIdentityDbContext CreateDbContext(string[] args) + { + // Load configuration + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddJsonFile("appsettings.Development.json", optional: true) + .AddEnvironmentVariables() + .Build(); + + // Use the same connection string key as in Startup.AddPBIdentityDbContext + var configurationPath = $"{DBEngineConstants.RootConnectionString}:Identity{DBEngineConstants.dbConnectionStringPrefix}"; + var connectionString = (args != null && args.Length > 0 && !string.IsNullOrEmpty(args[0])) + ? args[0] + : configuration[configurationPath]; + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(connectionString); + return new PBIdentityDbContext(optionsBuilder.Options); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PlanningBook.Identity.Infrastructure.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PlanningBook.Identity.Infrastructure.csproj new file mode 100644 index 00000000..a12bc53f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/PlanningBook.Identity.Infrastructure.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Startup.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Startup.cs new file mode 100644 index 00000000..97eef9d3 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Identity/PlanningBook.Identity.Infrastructure/Startup.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using PlanningBook.DBEngine.Constants; + +namespace PlanningBook.Identity.Infrastructure +{ + public static class Startup + { + public static void AddPBIdentityDbContext(this IServiceCollection services, IConfiguration configuration) + { + //TODO-Improve: Use DBEngine in BuildingBlock for add db connection + var test = $"{DBEngineConstants.RootConnectionString}:Identity{DBEngineConstants.dbConnectionStringPrefix}"; + services.AddDbContext(options => options.UseSqlServer(configuration[test])); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Controllers/InvoicesController.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Controllers/InvoicesController.cs new file mode 100644 index 00000000..daac5e09 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Controllers/InvoicesController.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Extensions; +using PlanningBook.Themes.Application.Domain.Invoices.Commands; +using Stripe; + +namespace PlanningBook.Themes.API.Controllers +{ + [Route("[controller]")] + [ApiController] + [Authorize(AuthenticationSchemes = "Bearer")] + public class InvoicesController( + IQueryExecutor _queryExecutor, + ICommandExecutor _commandExecutor) : ControllerBase + { + [HttpPost("checkout")] + public async Task>> CreateAsync([FromBody] CreateInvoiceCommand command) + { + var currentUserId = User.GetCurrentAccountId(); + command.UserId = currentUserId; + + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + + [HttpPost("payment-intent")] + public async Task>> CreateAsync([FromBody] CreatePaymentIntentCommand command) + { + var currentUserId = User.GetCurrentAccountId()??Guid.Empty; + command.UserId = currentUserId; + + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Controllers/StripeCustomersController.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Controllers/StripeCustomersController.cs new file mode 100644 index 00000000..4231df43 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Controllers/StripeCustomersController.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Extensions; +using PlanningBook.Themes.Application.Domain.Invoices.Commands; +using PlanningBook.Themes.Application.Domain.StripeCustomers.Commands; +using PlanningBook.Themes.Infrastructure.Entities; + +namespace PlanningBook.Themes.API.Controllers +{ + [Route("[controller]")] + [ApiController] + [Authorize(AuthenticationSchemes = "Bearer")] + public class StripeCustomersController( + IQueryExecutor _queryExecutor, + ICommandExecutor _commandExecutor) : ControllerBase + { + [HttpPost("create-stripe-customer")] + public async Task>> CreateStripeCustomerAsync() + { + var currentUserId = User.GetCurrentAccountId() ?? Guid.Empty; + var command = new CreateStripeCustomerCommand() + { + UserId = currentUserId + }; + + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + + [HttpPost("create-stripe-payment-method")] + public async Task>> CreateStripePaymentMethodAsync([FromBody] CreateCustomerPaymentMethodCommand command) + { + var currentUserId = User.GetCurrentAccountId() ?? Guid.Empty; + command.UserId = currentUserId; + + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + + [HttpPost("cancel-subscription")] + public async Task>> CancelSubscriptionAsync([FromBody] CancelSubscriptionCommand command) + { + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + + [HttpPut("update-customer")] + public async Task>> UpdateCustomerAsync([FromBody] UpdateStripeCustomerCommand command) + { + var currentUserId = User.GetCurrentAccountId() ?? Guid.Empty; + command.UserId = currentUserId; + + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Extensions/IServiceCollectionExtensions.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 00000000..3ca83601 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,62 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Application.Domain.Invoices.Commands; +using PlanningBook.Themes.Application.Domain.Invoices.Commands.Models; +using PlanningBook.Themes.Application.Domain.Invoices.Queries; +using PlanningBook.Themes.Application.Domain.Invoices.Queries.Models; +using PlanningBook.Themes.Application.Domain.StripeCustomers.Commands; +using PlanningBook.Themes.Application.Domain.StripeCustomers.Queries; +using PlanningBook.Themes.Infrastructure.Entities; +using Stripe; + +namespace PlanningBook.Themes.API.Extensions +{ + public static class IServiceCollectionExtensions + { + public static IServiceCollection AddRepositories(this IServiceCollection services) + { + #region Add Repositories + services.AddScoped(typeof(IEFRepository<,,>), typeof(EFRepository<,,>)); + services.AddScoped(typeof(IEFRepository<,,,>), typeof(EFRepository<,,,>)); + services.AddScoped(typeof(IEFClassRepository<,,>), typeof(EFClassRepository<,,>)); + services.AddScoped(typeof(IEFClassRepository<,,,>), typeof(EFClassRepository<,,,>)); + #endregion Add Repositories + + return services; + } + + public static IServiceCollection RegistryCommandQueryExecutor(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient(); + services.AddTransient(); + return services; + } + + public static IServiceCollection RegistryThemeModule(this IServiceCollection services, IConfiguration configuration) + { + #region Commands + // Invoices + services.AddScoped>, CreateInvoiceCommandHandler>(); + services.AddScoped>, UpdateInvoiceCommandHandler>(); + + // Stripe Customer - Payment Method + services.AddScoped>, CreateStripeCustomerCommandHandler>(); + services.AddScoped>, CreateCustomerPaymentMethodCommandHandler>(); + services.AddScoped>, UpdateStripeCustomerCommandHandler>(); + services.AddScoped>, CancelSubscriptionCommandHandler>(); + services.AddScoped>, ResumeSubscriptionCommandHandler>(); + services.AddScoped>, CreatePaymentIntentCommandHandler>(); + #endregion Commands + + #region Queries + // Invoices + services.AddScoped>>, GetUserInvoicesQueryHandler>(); + + // Stripe Customer - Payment method + services.AddScoped>, GetUserStripeCustomerQueryHandler>(); + #endregion Queries + return services; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/PlanningBook.Themes.API.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/PlanningBook.Themes.API.csproj new file mode 100644 index 00000000..8f0c9d8f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/PlanningBook.Themes.API.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/PlanningBook.Themes.API.http b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/PlanningBook.Themes.API.http new file mode 100644 index 00000000..861fee43 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/PlanningBook.Themes.API.http @@ -0,0 +1,6 @@ +@PlanningBook.Themes.API_HostAddress = http://localhost:5004 + +GET {{PlanningBook.Themes.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Program.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Program.cs new file mode 100644 index 00000000..bcf69ab4 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Program.cs @@ -0,0 +1,126 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using PlanningBook.Themes.API.Extensions; +using PlanningBook.Themes.Application.Models; +using PlanningBook.Themes.Application.Services; +using PlanningBook.Themes.Infrastructure; +using System.Text; + +var builder = WebApplication.CreateBuilder(args); +var configuration = builder.Configuration; +// Add services to the container. + +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowSpecificOrigins", builder => + { + builder.WithOrigins("http://localhost:4200") + .AllowAnyHeader() + .AllowAnyMethod(); + }); +}); + +builder.Services.AddControllers(); + +#region Add DContexts +builder.Services.AddPBThemeDbContext(configuration); +#endregion Add DbContexts + +#region Add Services +builder.Services + .AddRepositories() + .RegistryCommandQueryExecutor(configuration) + .RegistryThemeModule(configuration); +#endregion Add Services + +#region Add Authentication +builder.Services.AddAuthentication(o => +{ + o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) + .AddJwtBearer(o => + { + o.SaveToken = true; + o.TokenValidationParameters = new TokenValidationParameters() + { + SaveSigninToken = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])), + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero, + ValidateIssuer = true, + ValidIssuer = builder.Configuration["Jwt:Issuer"], + ValidateAudience = true, + ValidAudience = builder.Configuration["Jwt:Audience"], + }; + }); +builder.Services.AddAuthorization(); +#endregion Add Authentication + +#region Add Swagger +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen( + c => + { + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n + Enter 'Bearer' [space] and then your token in the text input below. + \r\n\r\nExample: 'Bearer 12345abcdef'", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header, + + }, + new List() + } + }); + } +); +#endregion Add Swagger + +#region Add Stripe +builder.Services.Configure(builder.Configuration.GetSection("Stripe")); +Stripe.StripeConfiguration.ApiKey = builder.Configuration["Stripe:SecretKey"]; +builder.Services.AddSingleton(); +#endregion Add Stripe + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseCors("AllowSpecificOrigins"); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Properties/launchSettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Properties/launchSettings.json new file mode 100644 index 00000000..328e161b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:33006", + "sslPort": 44383 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5004", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7166;http://localhost:5004", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/WeatherForecast.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/WeatherForecast.cs new file mode 100644 index 00000000..bc2e6298 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace PlanningBook.Themes.API +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/appsettings.Development.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/appsettings.Development.json new file mode 100644 index 00000000..86fc2e18 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/appsettings.Development.json @@ -0,0 +1,26 @@ +{ + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:5003" // Set your fixed port here + } + } + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "DatabaseConnectionStrings": { + "ThemeConnectionString": "Data Source=localhost;Initial Catalog=PlanningBookTheme;User Id=;Password=;TrustServerCertificate=True;" // CTY + }, + "Stripe": { + "SecretKey": "" + }, + "Jwt": { + "Secret": "MlKMklmnJV7c24A1gAtcrlD5AY0hPShCaqZym3aYBUM", + "Issuer": "http://localhost:5001", + "Audience": "http://localhost:5001" + } +} \ No newline at end of file diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/appsettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CancelSubscriptionCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CancelSubscriptionCommand.cs new file mode 100644 index 00000000..11e3c903 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CancelSubscriptionCommand.cs @@ -0,0 +1,32 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Themes.Application.Services; + +namespace PlanningBook.Themes.Application.Domain.Invoices.Commands +{ + public sealed class CancelSubscriptionCommand : ICommand> + { + public string SubscriptionId { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class CancelSubscriptionCommandHandler(StripePaymentService _stripeService) : ICommandHandler> + { + public async Task> HandleAsync(CancelSubscriptionCommand command, CancellationToken cancellationToken = default) + { + try + { + await _stripeService.CancelSubscriptionsAsyn(command.SubscriptionId); + + return CommandResult.Success(true); + } + catch (Exception ex) + { + return CommandResult.Failure($"{ex.Message.ToString()}"); + } + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CreateInvoiceCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CreateInvoiceCommand.cs new file mode 100644 index 00000000..24b257e1 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CreateInvoiceCommand.cs @@ -0,0 +1,82 @@ +using PlanningBook.Domain.Interfaces; +using PlanningBook.Domain; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; +using PlanningBook.Themes.Application.Services; +using PlanningBook.Themes.Infrastructure.Entities.Enums; +using PlanningBook.Themes.Application.Domain.Invoices.Commands.Models; +using Stripe.Checkout; + +namespace PlanningBook.Themes.Application.Domain.Invoices.Commands +{ + public sealed class CreateInvoiceCommand : ICommand> + { + public string OriginUrl { get; set; } + public Guid ProductId { get; set; } + public Guid? UserId { get; set; } + public decimal? Price { get; set; } + public bool IsUseStripePrice { get; set; } + public bool IsAutoSavePayment { get; set; } = false; + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class CreateInvoiceCommandHandler( + IEFRepository _invoiceRepository, + IEFRepository _productRepository, + IEFRepository _customerStripeRepository, + StripePaymentService _stripePaymentService) : ICommandHandler> + { + public async Task> HandleAsync(CreateInvoiceCommand command, CancellationToken cancellationToken = default) + { + var userExisted = await _customerStripeRepository.GetFirstAsync(x => x.UserId == command.UserId, cancellationToken); + if (userExisted == null) + return CommandResult.Failure("User Not Existed"); + + var productExited = await _productRepository.GetFirstAsync(x => x.Id == command.ProductId, cancellationToken); + if (productExited == null) + return CommandResult.Failure("Product Not Existed"); + + var invoice = new Invoice() + { + UserId = userExisted.UserId, + PaymentStatus = PaymentStatus.Pending, + TotalAmount = productExited.Price, + ActualyTotalAmout = productExited.Price, + ProductId = productExited.Id, + Notes = $"{DateTime.Now.ToString()} - {userExisted.StripeCustomerId}" + }; + await _invoiceRepository.AddAsync(invoice, cancellationToken); + await _invoiceRepository.SaveChangeAsync(cancellationToken); + + var actualyProductPrice = command.Price ?? productExited.Price; + var customerId = command.IsAutoSavePayment ? userExisted.StripeCustomerId : null; + Session session = null; + if (command.IsUseStripePrice) + session = await _stripePaymentService.CheckoutSessionAsync(command.OriginUrl, userExisted.UserId, invoice.Id, productExited.ProductType, 0, productExited.StripePriceId, null, customerId); + else + session = await _stripePaymentService.CheckoutSessionAsync(command.OriginUrl, userExisted.UserId, invoice.Id, productExited.ProductType, actualyProductPrice, null, null, customerId); + + if(session != null) + { + var result = new CreateInvoiceCommandResult() + { + InvoiceId = invoice.Id, + StripeSessionId = session.Id, + Mode = session.Mode, + ActuallyAmountTotal = session.AmountSubtotal, + AmountTotal = session.AmountTotal, + Currency = session.Currency, + Url = session.Url, + SubscriptionId = session.SubscriptionId + }; + return CommandResult.Success(result); + } + + return CommandResult.Failure("End Wrong!"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CreatePaymentIntentCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CreatePaymentIntentCommand.cs new file mode 100644 index 00000000..23ad495e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/CreatePaymentIntentCommand.cs @@ -0,0 +1,43 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Application.Services; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; +using Stripe; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace PlanningBook.Themes.Application.Domain.Invoices.Commands +{ + public sealed class CreatePaymentIntentCommand : ICommand> + { + public Guid UserId { get; set; } + public long Amount { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class CreatePaymentIntentCommandHandler( + IEFRepository _stripeCustomerRepository, + StripePaymentService _stripeService) : ICommandHandler> + { + public async Task> HandleAsync(CreatePaymentIntentCommand command, CancellationToken cancellationToken = default) + { + var userExsited = await _stripeCustomerRepository.GetFirstAsync(x => x.UserId == command.UserId); + if (userExsited == null) + return CommandResult.Failure("User Not Found!"); + + var result = await _stripeService.CreatePaymentIntentAsync(userExsited.StripeCustomerId, userExsited.StripePaymentMethodId, command.Amount); + + return CommandResult.Success(true); + + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/Models/CreateInvoiceCommandResult.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/Models/CreateInvoiceCommandResult.cs new file mode 100644 index 00000000..5504f98b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/Models/CreateInvoiceCommandResult.cs @@ -0,0 +1,14 @@ +namespace PlanningBook.Themes.Application.Domain.Invoices.Commands.Models +{ + public class CreateInvoiceCommandResult + { + public Guid InvoiceId { get; set; } + public string StripeSessionId { get; set; } + public string? SubscriptionId { get; set; } + public string Mode { get; set; } + public string Url { get; set; } + public string Currency { get; set; } + public long? ActuallyAmountTotal { get; set; } + public long? AmountTotal { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/ResumeSubscriptionCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/ResumeSubscriptionCommand.cs new file mode 100644 index 00000000..9de6918c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/ResumeSubscriptionCommand.cs @@ -0,0 +1,32 @@ +using PlanningBook.Domain.Interfaces; +using PlanningBook.Domain; +using PlanningBook.Themes.Application.Services; + +namespace PlanningBook.Themes.Application.Domain.Invoices.Commands +{ + public sealed class ResumeSubscriptionCommand : ICommand> + { + public string SubscriptionId { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class ResumeSubscriptionCommandHandler(StripePaymentService _stripeService) : ICommandHandler> + { + public async Task> HandleAsync(ResumeSubscriptionCommand command, CancellationToken cancellationToken = default) + { + try + { + await _stripeService.ResumeSubsriptionAsyn(command.SubscriptionId); + + return CommandResult.Success(true); + } + catch (Exception ex) + { + return CommandResult.Failure($"{ex.Message.ToString()}"); + } + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/UpdateInvoiceCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/UpdateInvoiceCommand.cs new file mode 100644 index 00000000..7a319a3b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Commands/UpdateInvoiceCommand.cs @@ -0,0 +1,40 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; +using PlanningBook.Themes.Infrastructure.Entities.Enums; + +namespace PlanningBook.Themes.Application.Domain.Invoices.Commands +{ + public sealed class UpdateInvoiceCommand : ICommand> + { + public Guid InvoiceId { get; set; } + public bool IsSuccess { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class UpdateInvoiceCommandHandler( + IEFClassRepository _invoiceRepository) : ICommandHandler> + { + public async Task> HandleAsync(UpdateInvoiceCommand command, CancellationToken cancellationToken = default) + { + var invoiceExisted = await _invoiceRepository.GetByIdAsync(command.InvoiceId, cancellationToken); + if (invoiceExisted == null) + return CommandResult.Failure("Invoice not existed"); + + if (command.IsSuccess) + invoiceExisted.PaymentStatus = PaymentStatus.Success; + else + invoiceExisted.PaymentStatus = PaymentStatus.Cancel; + + await _invoiceRepository.UpdateAsync(invoiceExisted, cancellationToken); + await _invoiceRepository.SaveChangeAsync(cancellationToken); + + return CommandResult.Success(invoiceExisted.Id); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Queries/GetUserInvoicesQuery.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Queries/GetUserInvoicesQuery.cs new file mode 100644 index 00000000..53804c06 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Queries/GetUserInvoicesQuery.cs @@ -0,0 +1,35 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Application.Domain.Invoices.Queries.Models; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; + +namespace PlanningBook.Themes.Application.Domain.Invoices.Queries +{ + public sealed class GetUserInvoicesQuery : IQuery>> + { + public Guid UserId { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class GetUserInvoicesQueryHandler( + IEFRepository _invoiceRepository) : IQueryHandler>> + { + public async Task>> HandleAsync(GetUserInvoicesQuery query, CancellationToken cancellationToken = default) + { + var invoices = await _invoiceRepository.GetAsync(x => x.UserId == query.UserId); + var results = invoices.Select(x => new UserInvoiceModel() + { + InvoiceId = x.Id, + Amount = x.ActualyTotalAmout, + Status = x.PaymentStatus.ToString() + }).ToList(); + + return QueryResult>.Success(results); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Queries/Models/UserInvoiceModel.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Queries/Models/UserInvoiceModel.cs new file mode 100644 index 00000000..18cb005b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/Invoices/Queries/Models/UserInvoiceModel.cs @@ -0,0 +1,9 @@ +namespace PlanningBook.Themes.Application.Domain.Invoices.Queries.Models +{ + public sealed class UserInvoiceModel + { + public Guid InvoiceId { get; set; } + public string Status { get; set; } + public decimal Amount { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/CreateCustomerPaymentMethodCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/CreateCustomerPaymentMethodCommand.cs new file mode 100644 index 00000000..8c85313e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/CreateCustomerPaymentMethodCommand.cs @@ -0,0 +1,53 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Application.Services; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; + +namespace PlanningBook.Themes.Application.Domain.StripeCustomers.Commands +{ + public sealed class CreateCustomerPaymentMethodCommand : ICommand> + { + public Guid UserId { get; set; } + public string? ByPassCode { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class CreateCustomerPaymentMethodCommandHandler( + IEFRepository _stripeCustomerRepository, + StripePaymentService _stripeService) : ICommandHandler> + { + public async Task> HandleAsync(CreateCustomerPaymentMethodCommand command, CancellationToken cancellationToken = default) + { + var customerExisted = await _stripeCustomerRepository.GetFirstAsync(x => x.UserId == command.UserId, cancellationToken); + if (customerExisted == null) + { + return CommandResult.Failure("Customer is not existed"); + } + + if (string.IsNullOrWhiteSpace(customerExisted.StripeCustomerId)) + { + var customerStripe = await _stripeService.CreateCustomerAsync(command.UserId); + if(customerStripe == null) + { + return CommandResult.Failure("Something went wrong!"); + } + + customerExisted.StripeCustomerId = customerStripe; + } + + var customerPayment = await _stripeService.CreatePaymentMethodAsync(command.ByPassCode); + if (customerPayment == null) + return CommandResult.Failure("Create Failed"); + + await _stripeCustomerRepository.UpdateAsync(customerExisted, cancellationToken); + await _stripeCustomerRepository.SaveChangeAsync(cancellationToken); + + return CommandResult.Success(customerPayment); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/CreateStripeCustomerCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/CreateStripeCustomerCommand.cs new file mode 100644 index 00000000..46af8fb5 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/CreateStripeCustomerCommand.cs @@ -0,0 +1,39 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Application.Services; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; + +namespace PlanningBook.Themes.Application.Domain.StripeCustomers.Commands +{ + public sealed class CreateStripeCustomerCommand : ICommand> + { + public Guid UserId { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class CreateStripeCustomerCommandHandler( + IEFRepository _stripeCustomerRepository, + StripePaymentService _stripePaymentService) : ICommandHandler> + { + public async Task> HandleAsync(CreateStripeCustomerCommand command, CancellationToken cancellationToken = default) + { + var stripeCustomerId = await _stripePaymentService.CreateCustomerAsync(command.UserId); + + var stripeCustomer = new StripeCustomer() + { + StripeCustomerId = stripeCustomerId, + UserId = command.UserId, + }; + + await _stripeCustomerRepository.AddAsync(stripeCustomer); + await _stripeCustomerRepository.SaveChangeAsync(); + + return CommandResult.Success(stripeCustomerId); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/UpdateStripeCustomerCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/UpdateStripeCustomerCommand.cs new file mode 100644 index 00000000..4784dd1c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Commands/UpdateStripeCustomerCommand.cs @@ -0,0 +1,51 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Application.Services; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; + +namespace PlanningBook.Themes.Application.Domain.StripeCustomers.Commands +{ + public sealed class UpdateStripeCustomerCommand : ICommand> + { + public Guid UserId { get; set; } + public string PaymentMethodId { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class UpdateStripeCustomerCommandHandler( + IEFRepository _stripeCustomerRepository, + StripePaymentService _stripePaymentSerice) : ICommandHandler> + { + public async Task> HandleAsync(UpdateStripeCustomerCommand command, CancellationToken cancellationToken = default) + { + var customer = await _stripeCustomerRepository.GetFirstAsync(x => x.UserId == command.UserId, cancellationToken); + if (customer == null) + { + return CommandResult.Failure("Customer not exited"); + } + + if (customer.StripePaymentMethodId == command.PaymentMethodId) + { + await _stripePaymentSerice.DetachPaymentMethodAsync(command.PaymentMethodId); + customer.StripePaymentMethodId = null; + } + else + { + if (!string.IsNullOrWhiteSpace(customer.StripePaymentMethodId)) + await _stripePaymentSerice.DetachPaymentMethodAsync(customer.StripePaymentMethodId); + await _stripePaymentSerice.AttachPaymentMethodAsync(customer.StripeCustomerId, command.PaymentMethodId); + customer.StripePaymentMethodId = command.PaymentMethodId; + } + + await _stripeCustomerRepository.UpdateAsync(customer, cancellationToken); + await _stripeCustomerRepository.SaveChangeAsync(cancellationToken); + + return CommandResult.Success(customer); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Queries/GetUserStripeCustomerQuery.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Queries/GetUserStripeCustomerQuery.cs new file mode 100644 index 00000000..1a71bfe1 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Domain/StripeCustomers/Queries/GetUserStripeCustomerQuery.cs @@ -0,0 +1,33 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Themes.Infrastructure; +using PlanningBook.Themes.Infrastructure.Entities; + +namespace PlanningBook.Themes.Application.Domain.StripeCustomers.Queries +{ + public sealed class GetUserStripeCustomerQuery : IQuery> + { + public Guid UserId { get; set; } + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + + public class GetUserStripeCustomerQueryHandler( + IEFRepository _stripeCustomerRepository) : IQueryHandler> + { + public async Task> HandleAsync(GetUserStripeCustomerQuery query, CancellationToken cancellationToken = default) + { + var result = await _stripeCustomerRepository.GetFirstAsync(x => x.UserId == query.UserId, cancellationToken); + + if (result == null) + { + QueryResult.Failure("User still haven't Stripe Customer information"); + } + + return QueryResult.Success(result); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Models/CheckoutSession.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Models/CheckoutSession.cs new file mode 100644 index 00000000..dcc4be3d --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Models/CheckoutSession.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlanningBook.Themes.Application.Models +{ + internal class CheckoutSession + { + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Models/StripeSettings.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Models/StripeSettings.cs new file mode 100644 index 00000000..e30c9389 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Models/StripeSettings.cs @@ -0,0 +1,7 @@ +namespace PlanningBook.Themes.Application.Models +{ + public class StripeSettings + { + public string SecretKey { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/PlanningBook.Themes.Application.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/PlanningBook.Themes.Application.csproj new file mode 100644 index 00000000..f6b30242 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/PlanningBook.Themes.Application.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Services/StripePaymentService.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Services/StripePaymentService.cs new file mode 100644 index 00000000..8134ff06 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Application/Services/StripePaymentService.cs @@ -0,0 +1,186 @@ +using PlanningBook.Themes.Infrastructure.Entities.Enums; +using Stripe; +using Stripe.Checkout; + +namespace PlanningBook.Themes.Application.Services +{ + public class StripePaymentService + { + private readonly SessionService _sessionService; + private readonly CustomerService _customerService; + private readonly PaymentMethodService _paymentMethodService; + private readonly SubscriptionService _subscriptionService; + private readonly PaymentIntentService _paymentIntentService; + public StripePaymentService() + { + _sessionService = new SessionService(); + _customerService = new CustomerService(); + _paymentMethodService = new PaymentMethodService(); + _subscriptionService = new SubscriptionService(); + _paymentIntentService = new PaymentIntentService(); + } + + public async Task CheckoutSessionAsync(string originUrl, Guid userId, Guid invoiceId, ProductType productType, decimal price, string? stripePriceId = null, string? stripeProductId = null, string customerId = null) + { + var mode = productType == ProductType.SubcriptionPlan ? "subscription" : "payment"; + var successUrl = $"{originUrl}/planning-books/success?invoiceId={invoiceId}"; + var cancelUrl = $"{originUrl}/planning-books/cancel?invoiceId={invoiceId}"; + var lineItems = !string.IsNullOrWhiteSpace(stripePriceId) ? + new List + { + new SessionLineItemOptions + { + Price = stripePriceId, + Quantity = 1 + } + } : + new List + { + new SessionLineItemOptions() + { + PriceData = new SessionLineItemPriceDataOptions() + { + Currency = "USD", + ProductData = new SessionLineItemPriceDataProductDataOptions() + { + Name = Guid.NewGuid().ToString(), + }, + UnitAmount = (long)price*100 + }, + Quantity = 1, + } + }; + + var metadata = new Dictionary(); + metadata["invoiceId"] = invoiceId.ToString(); + var checkoutSessionOptions = new SessionCreateOptions() + { + Mode = mode, + ClientReferenceId = Guid.NewGuid().ToString(), + SuccessUrl = successUrl, + CancelUrl = cancelUrl, + CustomerEmail = $"test{userId.ToString().Replace("-", "")}@mail.com", + LineItems = lineItems, + Metadata = metadata, + AllowPromotionCodes = true, + }; + + if(!string.IsNullOrWhiteSpace(customerId) && productType == ProductType.SubcriptionPlan) + { + checkoutSessionOptions.CustomerEmail = null; + checkoutSessionOptions.Customer = customerId; + checkoutSessionOptions.SavedPaymentMethodOptions = new SessionSavedPaymentMethodOptionsOptions() + { + PaymentMethodSave = "enabled", + AllowRedisplayFilters = new List() { "always" } + }; + } + + var stripeCheckoutSession = await _sessionService.CreateAsync(checkoutSessionOptions); + + return stripeCheckoutSession; + } + + public async Task CreateCustomerAsync(Guid userId) + { + var options = new CustomerCreateOptions() + { + Name = userId.ToString(), + Email = $"test{userId.ToString().Replace("-", "")}@mail.com" + }; + + var customer = await _customerService.CreateAsync(options); + if (customer != null) + return customer.Id; + + return string.Empty; + } + + public async Task CreatePaymentMethodAsync(string byPassToken) + { + // By Pass Token Using for Demo only + var options = new PaymentMethodCreateOptions(); + if (string.IsNullOrWhiteSpace(byPassToken)) + { + // Demo show error cant not direcly send card/payment infor from our server to Stripe. Need use Stripe.js to implement this workflow + options = new PaymentMethodCreateOptions() + { + Type = "card", + Card = new PaymentMethodCardOptions() + { + Number = "4242424242424242", + ExpMonth = 12, + ExpYear = 28, + Cvc = "767" + } + }; + } + else + { + options = new PaymentMethodCreateOptions() + { + Type = "card", + Card = new PaymentMethodCardOptions() + { + Token = byPassToken + }, + + }; + } + var paymentMethod = await _paymentMethodService.CreateAsync(options); + + return paymentMethod.Id; + } + + public async Task AttachPaymentMethodAsync(string customerStripeId, string paymentMethodId) + { + var options = new PaymentMethodAttachOptions() + { + Customer = customerStripeId + }; + + await _paymentMethodService.AttachAsync(paymentMethodId, options); + } + + public async Task DetachPaymentMethodAsync(string paymentMethodId) + { + await _paymentMethodService.DetachAsync(paymentMethodId); + } + + public async Task CancelSubscriptionsAsyn(string subscriptionId) + { + await _subscriptionService.CancelAsync(subscriptionId); + } + + public async Task ResumeSubsriptionAsyn(string subscriptionId) + { + var options = new SubscriptionResumeOptions + { + BillingCycleAnchor = SubscriptionBillingCycleAnchor.Now, + }; + await _subscriptionService.ResumeAsync(subscriptionId, options); + } + + public async Task CreatePaymentIntentAsync(string customerId, string paymentMethodId, long amount) + { + var options = new PaymentIntentCreateOptions() + { + Amount = amount, + Currency = "usd", + Customer = customerId, + PaymentMethod = paymentMethodId, + Confirm = true, + //ConfirmationMethod = "automatic", + ReturnUrl = "http://localhost:5003", + AutomaticPaymentMethods = new PaymentIntentAutomaticPaymentMethodsOptions() + { + Enabled = true, + AllowRedirects = "never" + } + + }; + + return await _paymentIntentService.CreateAsync(options); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/InvoiceConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/InvoiceConfiguration.cs new file mode 100644 index 00000000..3e6e13a9 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/InvoiceConfiguration.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Themes.Infrastructure.Entities.Configurations +{ + public class InvoiceConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/InvoiceDetailConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/InvoiceDetailConfiguration.cs new file mode 100644 index 00000000..e4f5999a --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/InvoiceDetailConfiguration.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Themes.Infrastructure.Entities.Configurations +{ + public class InvoiceDetailConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/ProductConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/ProductConfiguration.cs new file mode 100644 index 00000000..4929410a --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/ProductConfiguration.cs @@ -0,0 +1,72 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; +using PlanningBook.Themes.Infrastructure.Entities.Enums; + +namespace PlanningBook.Themes.Infrastructure.Entities.Configurations +{ + public class ProductConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.HasData( + // Subscriptions + new Product() + { + Id = new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), + StripeId = "prod_RGHOXdDumkw4WR", + Name = "Lifetime Plan", + Price = 710, + StripePriceId = "price_1QNkpFDkmueHRg9S4XBlzXWg", + ProductType = ProductType.SubcriptionPlan + }, + new Product() + { + Id = new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), + StripeId = "prod_RGHNxfzXdhiDTx", + Name = "Elite Plan", + Price = 294, + StripePriceId = "price_1QNkobDkmueHRg9SOjsRIR9U", + ProductType = ProductType.SubcriptionPlan + }, + new Product() + { + Id = new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), + StripeId = "prod_RGHLOw7fxZK5kZ", + Name = "Basic Plan", + Price = 150, + StripePriceId = "price_1QNkmkDkmueHRg9S9kzCsJK0", + ProductType = ProductType.SubcriptionPlan + }, + new Product() + { + Id = new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), + StripeId = "prod_RICKs6KQp8yTZN", + Name = "Theme One", + Price = 150, + ProductType = ProductType.Theme, + StripePriceId = "price_1QPbvhDkmueHRg9SYxUpGaE4" + }, + new Product() + { + Id = new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), + StripeId = "prod_RICKg7t3XaDv6z", + Name = "Theme Two", + Price = 250, + ProductType = ProductType.Theme, + StripePriceId = "price_1QPbw8DkmueHRg9SEP1p1iMK" + }, + new Product() + { + Id = new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + StripeId = "prod_RICLOCfsBhGqge", + Name = "Theme Three", + Price = 400, + ProductType = ProductType.Theme, + StripePriceId = "price_1QPbwlDkmueHRg9Smb83pb2L" + } + ); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/StripeCustomerConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/StripeCustomerConfiguration.cs new file mode 100644 index 00000000..94687092 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Configurations/StripeCustomerConfiguration.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlanningBook.Themes.Infrastructure.Entities.Configurations +{ + public class StripeCustomerConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/PaymentStatus.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/PaymentStatus.cs new file mode 100644 index 00000000..b7ccc3c5 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/PaymentStatus.cs @@ -0,0 +1,9 @@ +namespace PlanningBook.Themes.Infrastructure.Entities.Enums +{ + public enum PaymentStatus + { + Pending, + Success, + Cancel + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/ProductType.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/ProductType.cs new file mode 100644 index 00000000..509cc823 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/ProductType.cs @@ -0,0 +1,8 @@ +namespace PlanningBook.Themes.Infrastructure.Entities.Enums +{ + public enum ProductType + { + Theme, + SubcriptionPlan + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/SubscriptionType.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/SubscriptionType.cs new file mode 100644 index 00000000..b50c8f40 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Enums/SubscriptionType.cs @@ -0,0 +1,9 @@ +namespace PlanningBook.Themes.Infrastructure.Entities.Enums +{ + public enum SubscriptionType + { + Daily, + Monthly, + Annualy + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Invoice.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Invoice.cs new file mode 100644 index 00000000..90e3cffc --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Invoice.cs @@ -0,0 +1,24 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Themes.Infrastructure.Entities.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlanningBook.Themes.Infrastructure.Entities +{ + // DEMO only + public class Invoice : EntityBase, IDateAudited + { + public PaymentStatus PaymentStatus { get; set; } + public decimal TotalAmount { get; set; } + public decimal ActualyTotalAmout { get; set; } + public Guid UserId { get; set; } + public Guid ProductId { get; set; } + public string? Notes { get; set; } + public DateTime? CreatedDate { get; set; } + public DateTime? UpdatedDate { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/InvoiceDetail.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/InvoiceDetail.cs new file mode 100644 index 00000000..a059f970 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/InvoiceDetail.cs @@ -0,0 +1,15 @@ +using PlanningBook.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlanningBook.Themes.Infrastructure.Entities +{ + public class InvoiceDetail : EntityBase + { + public Guid InvoiceId { get; set; } + public Guid ProductId { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Product.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Product.cs new file mode 100644 index 00000000..54dab43e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/Product.cs @@ -0,0 +1,15 @@ +using PlanningBook.Domain; +using PlanningBook.Themes.Infrastructure.Entities.Enums; + +namespace PlanningBook.Themes.Infrastructure.Entities +{ + public class Product : EntityBase + { + // Demo Only + public string Name { get; set; } + public decimal Price { get; set; } + public ProductType ProductType { get; set; } + public string StripeId { get; set; } + public string StripePriceId { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/StripeCustomer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/StripeCustomer.cs new file mode 100644 index 00000000..d4e5c47b --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Entities/StripeCustomer.cs @@ -0,0 +1,11 @@ +using PlanningBook.Domain; + +namespace PlanningBook.Themes.Infrastructure.Entities +{ + public class StripeCustomer : EntityBase + { + public Guid UserId { get; set; } + public string StripeCustomerId { get; set; } + public string? StripePaymentMethodId { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241008155950_Initial_Theme_Database.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241008155950_Initial_Theme_Database.Designer.cs new file mode 100644 index 00000000..93498c73 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241008155950_Initial_Theme_Database.Designer.cs @@ -0,0 +1,171 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241008155950_Initial_Theme_Database")] + partial class Initial_Theme_Database + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Note") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityRelatedId") + .HasColumnType("uniqueidentifier"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PriceId"); + + b.ToTable("OrderDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.SubscriptionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.HasOne("PlanningBook.Themes.Infrastructure.Entities.Price", "Price") + .WithMany("OrderDetails") + .HasForeignKey("PriceId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Price"); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241008155950_Initial_Theme_Database.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241008155950_Initial_Theme_Database.cs new file mode 100644 index 00000000..cc29d409 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241008155950_Initial_Theme_Database.cs @@ -0,0 +1,131 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Initial_Theme_Database : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Note = table.Column(type: "nvarchar(max)", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: true), + UpdatedDate = table.Column(type: "datetime2", nullable: true), + CreatedBy = table.Column(type: "uniqueidentifier", nullable: false), + UpdatedBy = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Prices", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Value = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Prices", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ProductPrices", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ProductId = table.Column(type: "uniqueidentifier", nullable: false), + PriceId = table.Column(type: "uniqueidentifier", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: true), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProductPrices", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SubscriptionPlans", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true), + Type = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SubscriptionPlans", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Themes", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Themes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OrderDetails", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + EntityRelatedId = table.Column(type: "uniqueidentifier", nullable: false), + ProductType = table.Column(type: "int", nullable: false), + PriceId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderDetails", x => x.Id); + table.ForeignKey( + name: "FK_OrderDetails_Prices_PriceId", + column: x => x.PriceId, + principalTable: "Prices", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_OrderDetails_PriceId", + table: "OrderDetails", + column: "PriceId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OrderDetails"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "ProductPrices"); + + migrationBuilder.DropTable( + name: "SubscriptionPlans"); + + migrationBuilder.DropTable( + name: "Themes"); + + migrationBuilder.DropTable( + name: "Prices"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241009171546_Add_Price_Column_directly.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241009171546_Add_Price_Column_directly.Designer.cs new file mode 100644 index 00000000..1120ad11 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241009171546_Add_Price_Column_directly.Designer.cs @@ -0,0 +1,177 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241009171546_Add_Price_Column_directly")] + partial class Add_Price_Column_directly + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Note") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityRelatedId") + .HasColumnType("uniqueidentifier"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PriceId"); + + b.ToTable("OrderDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.SubscriptionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.HasOne("PlanningBook.Themes.Infrastructure.Entities.Price", "Price") + .WithMany("OrderDetails") + .HasForeignKey("PriceId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Price"); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241009171546_Add_Price_Column_directly.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241009171546_Add_Price_Column_directly.cs new file mode 100644 index 00000000..2ae5f7b0 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241009171546_Add_Price_Column_directly.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Add_Price_Column_directly : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Price", + table: "Themes", + type: "decimal(18,2)", + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "Price", + table: "SubscriptionPlans", + type: "decimal(18,2)", + nullable: false, + defaultValue: 0m); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Price", + table: "Themes"); + + migrationBuilder.DropColumn( + name: "Price", + table: "SubscriptionPlans"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241118160345_Add_payment_method_table.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241118160345_Add_payment_method_table.Designer.cs new file mode 100644 index 00000000..50f01354 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241118160345_Add_payment_method_table.Designer.cs @@ -0,0 +1,195 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241118160345_Add_payment_method_table")] + partial class Add_payment_method_table + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Note") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityRelatedId") + .HasColumnType("uniqueidentifier"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PriceId"); + + b.ToTable("OrderDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.SubscriptionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.UserPaymentMethod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripePaymentMethodId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("UserPaymentMethods", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.HasOne("PlanningBook.Themes.Infrastructure.Entities.Price", "Price") + .WithMany("OrderDetails") + .HasForeignKey("PriceId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Price"); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241118160345_Add_payment_method_table.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241118160345_Add_payment_method_table.cs new file mode 100644 index 00000000..69eb1dde --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241118160345_Add_payment_method_table.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Add_payment_method_table : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UserPaymentMethods", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + StripePaymentMethodId = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserPaymentMethods", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserPaymentMethods"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121073112_Add-payement-status-for-order-table.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121073112_Add-payement-status-for-order-table.Designer.cs new file mode 100644 index 00000000..2c7a8eae --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121073112_Add-payement-status-for-order-table.Designer.cs @@ -0,0 +1,201 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241121073112_Add-payement-status-for-order-table")] + partial class Addpayementstatusforordertable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Note") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityRelatedId") + .HasColumnType("uniqueidentifier"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PriceId"); + + b.ToTable("OrderDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.SubscriptionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.UserPaymentMethod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripePaymentMethodId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("UserPaymentMethods", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.HasOne("PlanningBook.Themes.Infrastructure.Entities.Price", "Price") + .WithMany("OrderDetails") + .HasForeignKey("PriceId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Price"); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121073112_Add-payement-status-for-order-table.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121073112_Add-payement-status-for-order-table.cs new file mode 100644 index 00000000..d613be8f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121073112_Add-payement-status-for-order-table.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Addpayementstatusforordertable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PaymentStatus", + table: "Orders", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "TotalPrice", + table: "Orders", + type: "decimal(18,2)", + nullable: false, + defaultValue: 0m); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PaymentStatus", + table: "Orders"); + + migrationBuilder.DropColumn( + name: "TotalPrice", + table: "Orders"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121100639_Update-schema-order-table.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121100639_Update-schema-order-table.Designer.cs new file mode 100644 index 00000000..2a2b9ec6 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121100639_Update-schema-order-table.Designer.cs @@ -0,0 +1,207 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241121100639_Update-schema-order-table")] + partial class Updateschemaordertable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Note") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityRelatedId") + .HasColumnType("uniqueidentifier"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PriceId"); + + b.ToTable("OrderDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.SubscriptionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.UserPaymentMethod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripePaymentMethodId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("UserPaymentMethods", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.HasOne("PlanningBook.Themes.Infrastructure.Entities.Price", "Price") + .WithMany("OrderDetails") + .HasForeignKey("PriceId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Price"); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121100639_Update-schema-order-table.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121100639_Update-schema-order-table.cs new file mode 100644 index 00000000..f84e34d2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121100639_Update-schema-order-table.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Updateschemaordertable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ProductId", + table: "Orders", + type: "uniqueidentifier", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "ProductType", + table: "Orders", + type: "int", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ProductId", + table: "Orders"); + + migrationBuilder.DropColumn( + name: "ProductType", + table: "Orders"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191234_Add_seed_data.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191234_Add_seed_data.Designer.cs new file mode 100644 index 00000000..92cf9078 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191234_Add_seed_data.Designer.cs @@ -0,0 +1,256 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241121191234_Add_seed_data")] + partial class Add_seed_data + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Note") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityRelatedId") + .HasColumnType("uniqueidentifier"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PriceId"); + + b.ToTable("OrderDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.SubscriptionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans", (string)null); + + b.HasData( + new + { + Id = new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), + Description = "Basic", + Name = "Basic", + Price = 150m, + Type = 0 + }, + new + { + Id = new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), + Description = "Elite", + Name = "Elite", + Price = 294m, + Type = 0 + }, + new + { + Id = new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), + Description = "Lifetime", + Name = "Lifetime", + Price = 710m, + Type = 0 + }); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + + b.HasData( + new + { + Id = new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), + Description = "Black", + Name = "Black", + Price = 150m + }, + new + { + Id = new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), + Description = "White", + Name = "White", + Price = 250m + }, + new + { + Id = new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + Description = "Black", + Name = "Black", + Price = 400m + }); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.UserPaymentMethod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripePaymentMethodId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("UserPaymentMethods", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.HasOne("PlanningBook.Themes.Infrastructure.Entities.Price", "Price") + .WithMany("OrderDetails") + .HasForeignKey("PriceId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Price"); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191234_Add_seed_data.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191234_Add_seed_data.cs new file mode 100644 index 00000000..eafe56e2 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191234_Add_seed_data.cs @@ -0,0 +1,71 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Add_seed_data : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.InsertData( + table: "SubscriptionPlans", + columns: new[] { "Id", "Description", "Name", "Price", "Type" }, + values: new object[,] + { + { new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), "Lifetime", "Lifetime", 710m, 0 }, + { new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), "Basic", "Basic", 150m, 0 }, + { new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), "Elite", "Elite", 294m, 0 } + }); + + migrationBuilder.InsertData( + table: "Themes", + columns: new[] { "Id", "Description", "Name", "Price" }, + values: new object[,] + { + { new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), "White", "White", 250m }, + { new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), "Black", "Black", 150m }, + { new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), "Black", "Black", 400m } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "SubscriptionPlans", + keyColumn: "Id", + keyValue: new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840")); + + migrationBuilder.DeleteData( + table: "SubscriptionPlans", + keyColumn: "Id", + keyValue: new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef")); + + migrationBuilder.DeleteData( + table: "SubscriptionPlans", + keyColumn: "Id", + keyValue: new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9")); + + migrationBuilder.DeleteData( + table: "Themes", + keyColumn: "Id", + keyValue: new Guid("27784869-292e-47e8-be5f-311d7a4aaf14")); + + migrationBuilder.DeleteData( + table: "Themes", + keyColumn: "Id", + keyValue: new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13")); + + migrationBuilder.DeleteData( + table: "Themes", + keyColumn: "Id", + keyValue: new Guid("eea6122c-c5d8-40f1-a44a-766008241255")); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191543_Add_seed_data_2.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191543_Add_seed_data_2.Designer.cs new file mode 100644 index 00000000..94c627b7 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191543_Add_seed_data_2.Designer.cs @@ -0,0 +1,256 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241121191543_Add_seed_data_2")] + partial class Add_seed_data_2 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Note") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityRelatedId") + .HasColumnType("uniqueidentifier"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PriceId"); + + b.ToTable("OrderDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PriceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.SubscriptionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans", (string)null); + + b.HasData( + new + { + Id = new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), + Description = "Basic", + Name = "Basic", + Price = 150m, + Type = 0 + }, + new + { + Id = new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), + Description = "Elite", + Name = "Elite", + Price = 294m, + Type = 0 + }, + new + { + Id = new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), + Description = "Lifetime", + Name = "Lifetime", + Price = 710m, + Type = 0 + }); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + + b.HasData( + new + { + Id = new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), + Description = "Black", + Name = "Black", + Price = 150m + }, + new + { + Id = new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), + Description = "White", + Name = "White", + Price = 250m + }, + new + { + Id = new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + Description = "Rain", + Name = "Rain", + Price = 400m + }); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.UserPaymentMethod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripePaymentMethodId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("UserPaymentMethods", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.OrderDetail", b => + { + b.HasOne("PlanningBook.Themes.Infrastructure.Entities.Price", "Price") + .WithMany("OrderDetails") + .HasForeignKey("PriceId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Price"); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Price", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191543_Add_seed_data_2.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191543_Add_seed_data_2.cs new file mode 100644 index 00000000..7d6a5b48 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241121191543_Add_seed_data_2.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Add_seed_data_2 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "Themes", + keyColumn: "Id", + keyValue: new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + columns: new[] { "Description", "Name" }, + values: new object[] { "Rain", "Rain" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "Themes", + keyColumn: "Id", + keyValue: new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + columns: new[] { "Description", "Name" }, + values: new object[] { "Black", "Black" }); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241127045617_Change_Theme_Database_Schema.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241127045617_Change_Theme_Database_Schema.Designer.cs new file mode 100644 index 00000000..6a7f8af3 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241127045617_Change_Theme_Database_Schema.Designer.cs @@ -0,0 +1,189 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241127045617_Change_Theme_Database_Schema")] + partial class Change_Theme_Database_Schema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.CustomerStripe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripeCustomerId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentMethodId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("CustomerStripes", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualyTotalAmout") + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("TotalAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("Invoices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.InvoiceDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("InvoiceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("InvoiceDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("StripeId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePriceId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + Id = new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), + Name = "Lifetime Plan", + Price = 710m, + ProductType = 1, + StripeId = "prod_RGHOXdDumkw4WR", + StripePriceId = "price_1QNkpFDkmueHRg9S4XBlzXWg" + }, + new + { + Id = new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), + Name = "Elite Plan", + Price = 294m, + ProductType = 1, + StripeId = "prod_RGHNxfzXdhiDTx", + StripePriceId = "price_1QNkobDkmueHRg9SOjsRIR9U" + }, + new + { + Id = new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), + Name = "Basic Plan", + Price = 150m, + ProductType = 1, + StripeId = "prod_RGHLOw7fxZK5kZ", + StripePriceId = "price_1QNkmkDkmueHRg9S9kzCsJK0" + }, + new + { + Id = new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), + Name = "Theme One", + Price = 150m, + ProductType = 0, + StripeId = "prod_RICKs6KQp8yTZN", + StripePriceId = "price_1QPbvhDkmueHRg9SYxUpGaE4" + }, + new + { + Id = new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), + Name = "Theme Two", + Price = 250m, + ProductType = 0, + StripeId = "prod_RICKg7t3XaDv6z", + StripePriceId = "price_1QPbw8DkmueHRg9SEP1p1iMK" + }, + new + { + Id = new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + Name = "Theme Three", + Price = 400m, + ProductType = 0, + StripeId = "prod_RICLOCfsBhGqge", + StripePriceId = "price_1QPbwlDkmueHRg9Smb83pb2L" + }); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241127045617_Change_Theme_Database_Schema.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241127045617_Change_Theme_Database_Schema.cs new file mode 100644 index 00000000..3e2c8a49 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241127045617_Change_Theme_Database_Schema.cs @@ -0,0 +1,262 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Change_Theme_Database_Schema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OrderDetails"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "ProductPrices"); + + migrationBuilder.DropTable( + name: "SubscriptionPlans"); + + migrationBuilder.DropTable( + name: "Themes"); + + migrationBuilder.DropTable( + name: "UserPaymentMethods"); + + migrationBuilder.DropTable( + name: "Prices"); + + migrationBuilder.CreateTable( + name: "CustomerStripes", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + StripeCustomerId = table.Column(type: "nvarchar(max)", nullable: false), + StripePaymentMethodId = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomerStripes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "InvoiceDetails", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + InvoiceId = table.Column(type: "uniqueidentifier", nullable: false), + ProductId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_InvoiceDetails", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Invoices", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + PaymentStatus = table.Column(type: "int", nullable: false), + TotalAmount = table.Column(type: "decimal(18,2)", nullable: false), + ActualyTotalAmout = table.Column(type: "decimal(18,2)", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + ProductId = table.Column(type: "uniqueidentifier", nullable: false), + Notes = table.Column(type: "nvarchar(max)", nullable: true), + CreatedDate = table.Column(type: "datetime2", nullable: true), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Invoices", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Price = table.Column(type: "decimal(18,2)", nullable: false), + ProductType = table.Column(type: "int", nullable: false), + StripeId = table.Column(type: "nvarchar(max)", nullable: false), + StripePriceId = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + }); + + migrationBuilder.InsertData( + table: "Products", + columns: new[] { "Id", "Name", "Price", "ProductType", "StripeId", "StripePriceId" }, + values: new object[,] + { + { new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), "Lifetime Plan", 710m, 1, "prod_RGHOXdDumkw4WR", "price_1QNkpFDkmueHRg9S4XBlzXWg" }, + { new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), "Theme Two", 250m, 0, "prod_RICKg7t3XaDv6z", "price_1QPbw8DkmueHRg9SEP1p1iMK" }, + { new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), "Basic Plan", 150m, 1, "prod_RGHLOw7fxZK5kZ", "price_1QNkmkDkmueHRg9S9kzCsJK0" }, + { new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), "Theme One", 150m, 0, "prod_RICKs6KQp8yTZN", "price_1QPbvhDkmueHRg9SYxUpGaE4" }, + { new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), "Elite Plan", 294m, 1, "prod_RGHNxfzXdhiDTx", "price_1QNkobDkmueHRg9SOjsRIR9U" }, + { new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), "Theme Three", 400m, 0, "prod_RICLOCfsBhGqge", "price_1QPbwlDkmueHRg9Smb83pb2L" } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CustomerStripes"); + + migrationBuilder.DropTable( + name: "InvoiceDetails"); + + migrationBuilder.DropTable( + name: "Invoices"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + CreatedBy = table.Column(type: "uniqueidentifier", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: true), + Note = table.Column(type: "nvarchar(max)", nullable: false), + PaymentStatus = table.Column(type: "int", nullable: false), + ProductId = table.Column(type: "uniqueidentifier", nullable: false), + ProductType = table.Column(type: "int", nullable: false), + TotalPrice = table.Column(type: "decimal(18,2)", nullable: false), + UpdatedBy = table.Column(type: "uniqueidentifier", nullable: false), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Prices", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Value = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Prices", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ProductPrices", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: true), + PriceId = table.Column(type: "uniqueidentifier", nullable: false), + ProductId = table.Column(type: "uniqueidentifier", nullable: false), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProductPrices", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SubscriptionPlans", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Price = table.Column(type: "decimal(18,2)", nullable: false), + Type = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SubscriptionPlans", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Themes", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Price = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Themes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "UserPaymentMethods", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + StripePaymentMethodId = table.Column(type: "nvarchar(max)", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserPaymentMethods", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OrderDetails", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + PriceId = table.Column(type: "uniqueidentifier", nullable: false), + EntityRelatedId = table.Column(type: "uniqueidentifier", nullable: false), + ProductType = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderDetails", x => x.Id); + table.ForeignKey( + name: "FK_OrderDetails_Prices_PriceId", + column: x => x.PriceId, + principalTable: "Prices", + principalColumn: "Id"); + }); + + migrationBuilder.InsertData( + table: "SubscriptionPlans", + columns: new[] { "Id", "Description", "Name", "Price", "Type" }, + values: new object[,] + { + { new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), "Lifetime", "Lifetime", 710m, 0 }, + { new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), "Basic", "Basic", 150m, 0 }, + { new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), "Elite", "Elite", 294m, 0 } + }); + + migrationBuilder.InsertData( + table: "Themes", + columns: new[] { "Id", "Description", "Name", "Price" }, + values: new object[,] + { + { new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), "White", "White", 250m }, + { new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), "Black", "Black", 150m }, + { new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), "Rain", "Rain", 400m } + }); + + migrationBuilder.CreateIndex( + name: "IX_OrderDetails_PriceId", + table: "OrderDetails", + column: "PriceId"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241128092440_Update-schema.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241128092440_Update-schema.Designer.cs new file mode 100644 index 00000000..995621e8 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241128092440_Update-schema.Designer.cs @@ -0,0 +1,188 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + [Migration("20241128092440_Update-schema")] + partial class Updateschema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualyTotalAmout") + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("TotalAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("Invoices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.InvoiceDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("InvoiceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("InvoiceDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("StripeId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePriceId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + Id = new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), + Name = "Lifetime Plan", + Price = 710m, + ProductType = 1, + StripeId = "prod_RGHOXdDumkw4WR", + StripePriceId = "price_1QNkpFDkmueHRg9S4XBlzXWg" + }, + new + { + Id = new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), + Name = "Elite Plan", + Price = 294m, + ProductType = 1, + StripeId = "prod_RGHNxfzXdhiDTx", + StripePriceId = "price_1QNkobDkmueHRg9SOjsRIR9U" + }, + new + { + Id = new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), + Name = "Basic Plan", + Price = 150m, + ProductType = 1, + StripeId = "prod_RGHLOw7fxZK5kZ", + StripePriceId = "price_1QNkmkDkmueHRg9S9kzCsJK0" + }, + new + { + Id = new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), + Name = "Theme One", + Price = 150m, + ProductType = 0, + StripeId = "prod_RICKs6KQp8yTZN", + StripePriceId = "price_1QPbvhDkmueHRg9SYxUpGaE4" + }, + new + { + Id = new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), + Name = "Theme Two", + Price = 250m, + ProductType = 0, + StripeId = "prod_RICKg7t3XaDv6z", + StripePriceId = "price_1QPbw8DkmueHRg9SEP1p1iMK" + }, + new + { + Id = new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + Name = "Theme Three", + Price = 400m, + ProductType = 0, + StripeId = "prod_RICLOCfsBhGqge", + StripePriceId = "price_1QPbwlDkmueHRg9Smb83pb2L" + }); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.StripeCustomer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripeCustomerId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentMethodId") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("StripeCustomers", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241128092440_Update-schema.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241128092440_Update-schema.cs new file mode 100644 index 00000000..319ca02a --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/20241128092440_Update-schema.cs @@ -0,0 +1,53 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + /// + public partial class Updateschema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CustomerStripes"); + + migrationBuilder.CreateTable( + name: "StripeCustomers", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + StripeCustomerId = table.Column(type: "nvarchar(max)", nullable: false), + StripePaymentMethodId = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_StripeCustomers", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "StripeCustomers"); + + migrationBuilder.CreateTable( + name: "CustomerStripes", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + StripeCustomerId = table.Column(type: "nvarchar(max)", nullable: false), + StripePaymentMethodId = table.Column(type: "nvarchar(max)", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomerStripes", x => x.Id); + }); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/PBThemeDbContextModelSnapshot.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/PBThemeDbContextModelSnapshot.cs new file mode 100644 index 00000000..62cbf7b7 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/Migrations/PBThemeDbContextModelSnapshot.cs @@ -0,0 +1,185 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Themes.Infrastructure; + +#nullable disable + +namespace PlanningBook.Themes.Infrastructure.Migrations +{ + [DbContext(typeof(PBThemeDbContext))] + partial class PBThemeDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualyTotalAmout") + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("TotalAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("Invoices", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.InvoiceDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("InvoiceId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("InvoiceDetails", (string)null); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("StripeId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePriceId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + Id = new Guid("1fc13c2a-27e8-45cb-95dd-9dfd924db840"), + Name = "Lifetime Plan", + Price = 710m, + ProductType = 1, + StripeId = "prod_RGHOXdDumkw4WR", + StripePriceId = "price_1QNkpFDkmueHRg9S4XBlzXWg" + }, + new + { + Id = new Guid("b677c4c6-669b-43ca-9897-83b5cb1c0cd9"), + Name = "Elite Plan", + Price = 294m, + ProductType = 1, + StripeId = "prod_RGHNxfzXdhiDTx", + StripePriceId = "price_1QNkobDkmueHRg9SOjsRIR9U" + }, + new + { + Id = new Guid("371cffea-2b1e-4c4d-aec8-cffd1ae43fef"), + Name = "Basic Plan", + Price = 150m, + ProductType = 1, + StripeId = "prod_RGHLOw7fxZK5kZ", + StripePriceId = "price_1QNkmkDkmueHRg9S9kzCsJK0" + }, + new + { + Id = new Guid("835bc37f-8891-48da-9f01-4bfcbf50ab13"), + Name = "Theme One", + Price = 150m, + ProductType = 0, + StripeId = "prod_RICKs6KQp8yTZN", + StripePriceId = "price_1QPbvhDkmueHRg9SYxUpGaE4" + }, + new + { + Id = new Guid("27784869-292e-47e8-be5f-311d7a4aaf14"), + Name = "Theme Two", + Price = 250m, + ProductType = 0, + StripeId = "prod_RICKg7t3XaDv6z", + StripePriceId = "price_1QPbw8DkmueHRg9SEP1p1iMK" + }, + new + { + Id = new Guid("eea6122c-c5d8-40f1-a44a-766008241255"), + Name = "Theme Three", + Price = 400m, + ProductType = 0, + StripeId = "prod_RICLOCfsBhGqge", + StripePriceId = "price_1QPbwlDkmueHRg9Smb83pb2L" + }); + }); + + modelBuilder.Entity("PlanningBook.Themes.Infrastructure.Entities.StripeCustomer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("StripeCustomerId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentMethodId") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("StripeCustomers", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PBThemeDbContext.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PBThemeDbContext.cs new file mode 100644 index 00000000..46228d46 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PBThemeDbContext.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using System.Reflection; + +namespace PlanningBook.Themes.Infrastructure +{ + public class PBThemeDbContext : DbContext + { + public PBThemeDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PBThemeDbContextFactory.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PBThemeDbContextFactory.cs new file mode 100644 index 00000000..fa11f856 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PBThemeDbContextFactory.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using PlanningBook.DBEngine.Constants; + +namespace PlanningBook.Themes.Infrastructure +{ + public class PBThemeDbContextFactory : IDesignTimeDbContextFactory + { + public PBThemeDbContext CreateDbContext(string[] args) + { + // Load configuration + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddJsonFile("appsettings.Development.json", optional: true) + .AddEnvironmentVariables() + .Build(); + + var configurationPath = $"{DBEngineConstants.RootConnectionString}:Theme{DBEngineConstants.dbConnectionStringPrefix}"; + var connectionString = (args != null && args.Length > 0 && !string.IsNullOrEmpty(args[0])) + ? args[0] + : configuration[configurationPath]; + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(connectionString); + return new PBThemeDbContext(optionsBuilder.Options); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PlanningBook.Themes.Infrastructure.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PlanningBook.Themes.Infrastructure.csproj new file mode 100644 index 00000000..89a87508 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/PlanningBook.Themes.Infrastructure.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/StartUp.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/StartUp.cs new file mode 100644 index 00000000..7b650e5c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Themes/PlanningBook.Themes.Infrastructure/StartUp.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using PlanningBook.DBEngine.Constants; + +namespace PlanningBook.Themes.Infrastructure +{ + public static class Startup + { + public static void AddPBThemeDbContext(this IServiceCollection services, IConfiguration configuration) + { + //TODO-Improve: Use DBEngine in BuildingBlock for add db connection + var test = $"{DBEngineConstants.RootConnectionString}:Theme{DBEngineConstants.dbConnectionStringPrefix}"; + services.AddDbContext(options => options.UseSqlServer(configuration[test])); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/ExposureControllers/ExposurePersonController.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/ExposureControllers/ExposurePersonController.cs new file mode 100644 index 00000000..abc482b5 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/ExposureControllers/ExposurePersonController.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Users.Application.Persons.Commands; + +namespace PlanningBook.Users.API.Controllers.ExposureControllers +{ + // TODO: Add secret key to call Server-to-Server + [Route("api/[controller]")] + [ApiController] + [AllowAnonymous] + public class ExposurePersonController : ControllerBase + { + private readonly IQueryExecutor _queryExecutor; + private readonly ICommandExecutor _commandExecutor; + + public ExposurePersonController(IQueryExecutor queryExecutor, ICommandExecutor commandExecutor) + { + _queryExecutor = queryExecutor; + _commandExecutor = commandExecutor; + } + + [HttpPost("Create")] + public async Task>> CreatePersonBySystem([FromBody] CreatePersonCommand command) + { + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/HealthCheckController.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/HealthCheckController.cs new file mode 100644 index 00000000..8589755e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/HealthCheckController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Users.API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class HealthCheckController : ControllerBase + { + private readonly IQueryExecutor _queryExecutor; + private readonly ICommandExecutor _commandExecutor; + + public HealthCheckController( + IQueryExecutor queryExecutor, + ICommandExecutor commandExecutor) + { + _queryExecutor = queryExecutor; + _commandExecutor = commandExecutor; + } + + [AllowAnonymous] + [HttpGet("NonAuth")] + public ActionResult NoAuth() + { + return Ok("Still Live"); + } + + [HttpGet("HasAuth")] + [Authorize(AuthenticationSchemes = "Bearer")] + public ActionResult HasAuth() + { + return Ok("Still Live"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/PersonController.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/PersonController.cs new file mode 100644 index 00000000..e94dacf1 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Controllers/PersonController.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Users.Application.Persons.Commands; + +namespace PlanningBook.Users.API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + [Authorize(AuthenticationSchemes = "Bearer")] + public class PersonController : ControllerBase + { + private readonly IQueryExecutor _queryExecutor; + private readonly ICommandExecutor _commandExecutor; + + public PersonController(IQueryExecutor queryExecutor, ICommandExecutor commandExecutor) + { + _queryExecutor = queryExecutor; + _commandExecutor = commandExecutor; + } + + [HttpPost("Create")] + public async Task>> SignIn([FromBody] CreatePersonCommand command) + { + var result = await _commandExecutor.ExecuteAsync(command); + + if (result.IsSuccess) + return Ok(result); + else + return BadRequest(result); + } + + [HttpGet] + public async Task> GetPerson() + { + var user = User.Identity; + + return Ok("Test"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Extensions/IServiceCollectionExtensions.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 00000000..9526659e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,39 @@ +using PlanningBook.Domain.Interfaces; +using PlanningBook.Domain; +using PlanningBook.Repository.EF; +using PlanningBook.Users.Application.Persons.Commands; + +namespace PlanningBook.Users.API.Extensions +{ + public static class IServiceCollectionExtensions + { + public static IServiceCollection AddRepositories(this IServiceCollection services) + { + #region Add Repositories + services.AddScoped(typeof(IEFRepository<,,>), typeof(EFRepository<,,>)); + services.AddScoped(typeof(IEFRepository<,,,>), typeof(EFRepository<,,,>)); + services.AddScoped(typeof(IEFClassRepository<,,>), typeof(EFClassRepository<,,>)); + services.AddScoped(typeof(IEFClassRepository<,,,>), typeof(EFClassRepository<,,,>)); + #endregion Add Repositories + + return services; + } + + public static IServiceCollection RegistryCommandQueryExecutor(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient(); + services.AddTransient(); + return services; + } + + public static IServiceCollection RegistryPersonModule(this IServiceCollection services, IConfiguration configuration) + { + + #region Commands + services.AddScoped>, CreatePersonCommandHandler>(); + #endregion Commands + + return services; + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/PlanningBook.Users.API.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/PlanningBook.Users.API.csproj new file mode 100644 index 00000000..3bd91ac7 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/PlanningBook.Users.API.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/PlanningBook.Users.API.http b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/PlanningBook.Users.API.http new file mode 100644 index 00000000..da9ed6a8 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/PlanningBook.Users.API.http @@ -0,0 +1,6 @@ +@PlanningBook.Users.API_HostAddress = http://localhost:5067 + +GET {{PlanningBook.Users.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Program.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Program.cs new file mode 100644 index 00000000..b9679a99 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Program.cs @@ -0,0 +1,130 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using PlanningBook.Users.API.Extensions; +using PlanningBook.Users.Infrastructure; +using System.Net; +using System.Text; + +var builder = WebApplication.CreateBuilder(args); +var configuration = builder.Configuration; + +// Add services to the container. +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowSpecificOrigins", builder => + { + builder.WithOrigins("http://localhost:4200") + .AllowAnyHeader() + .AllowAnyMethod(); + }); +}); + +builder.Services.AddControllers(); + +#region Add DContexts +builder.Services.AddPBPersonDbContext(configuration); +#endregion Add DbContexts + +#region Add Services +builder.Services + .AddRepositories() + .RegistryCommandQueryExecutor(configuration) + .RegistryPersonModule(configuration); +#endregion Add Services + +#region Add Authentication +builder.Services.AddAuthentication(o => +{ + o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) + .AddJwtBearer(o => + { + o.SaveToken = true; + o.TokenValidationParameters = new TokenValidationParameters() + { + SaveSigninToken = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])), + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero, + ValidateIssuer = true, + ValidIssuer = builder.Configuration["Jwt:Issuer"], + ValidateAudience = true, + ValidAudience = builder.Configuration["Jwt:Audience"], + }; + }); +builder.Services.AddAuthorization(); +#endregion Add Authentication + +#region Add Swagger +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen( + c => + { + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n + Enter 'Bearer' [space] and then your token in the text input below. + \r\n\r\nExample: 'Bearer 12345abcdef'", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header, + + }, + new List() + } + }); + } +); +#endregion Add Swagger + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.Use(async (context, next) => +{ + await next(); + + if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized) // 401 + { + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync("NO"); + } +}); + +app.UseHttpsRedirection(); + +app.UseCors("AllowSpecificOrigins"); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Properties/launchSettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Properties/launchSettings.json new file mode 100644 index 00000000..9c4c5572 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:34767", + "sslPort": 44376 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5067", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7087;http://localhost:5067", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/appsettings.Development.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/appsettings.Development.json new file mode 100644 index 00000000..e148b710 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/appsettings.Development.json @@ -0,0 +1,23 @@ +{ + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:5002" // Set your fixed port here + } + } + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "DatabaseConnectionStrings": { + "PersonConnectionString": "Data Source=localhost;Initial Catalog=PlanningBookPerson;User Id=;Password=;TrustServerCertificate=True;" + }, + "Jwt": { + "Secret": "MlKMklmnJV7c24A1gAtcrlD5AY0hPShCaqZym3aYBUM", + "Issuer": "http://localhost:5001", + "Audience": "http://localhost:5001" + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/appsettings.json b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Application/Persons/Commands/CreatePersonCommand.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Application/Persons/Commands/CreatePersonCommand.cs new file mode 100644 index 00000000..3267c121 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Application/Persons/Commands/CreatePersonCommand.cs @@ -0,0 +1,55 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; +using PlanningBook.Repository.EF; +using PlanningBook.Users.Infrastructure; +using PlanningBook.Users.Infrastructure.Entites; + +namespace PlanningBook.Users.Application.Persons.Commands +{ + #region Command Model + public sealed class CreatePersonCommand : ICommand> + { + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public string? PhoneNumber { get; set; } + + public CreatePersonCommand(string? firstName, string? lastName, string? email, string? phoneNumber) + { + FirstName = firstName; + LastName = lastName; + Email = email; + PhoneNumber = phoneNumber; + } + + public ValidationResult GetValidationResult() + { + return ValidationResult.Success(); + } + } + #endregion Command Model + + #region Command Handler + public sealed class CreatePersonCommandHandler(IEFRepository _personRepository) : ICommandHandler> + { + public async Task> HandleAsync(CreatePersonCommand command, CancellationToken cancellationToken = default) + { + if (command == null || !command.GetValidationResult().IsValid) + return CommandResult.Failure(null, null); + + var person = new Person() + { + FirstName = command?.FirstName, + LastName = command?.LastName, + Email = command?.Email, + PhoneNumber = command?.PhoneNumber + }; + + await _personRepository.AddAsync(person, cancellationToken); + await _personRepository.SaveChangeAsync(cancellationToken); + + return CommandResult.Success(person.Id); + } + } + #endregion Command Handler +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Application/PlanningBook.Users.Application.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Application/PlanningBook.Users.Application.csproj new file mode 100644 index 00000000..2cde990c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Application/PlanningBook.Users.Application.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Entites/Configurations/PersonConfiguration.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Entites/Configurations/PersonConfiguration.cs new file mode 100644 index 00000000..9634c52c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Entites/Configurations/PersonConfiguration.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlanningBook.DBEngine; + +namespace PlanningBook.Users.Infrastructure.Entites.Configurations +{ + public class PersonConfiguration : BaseRelationDbEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Entites/Person.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Entites/Person.cs new file mode 100644 index 00000000..4a74ad2e --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Entites/Person.cs @@ -0,0 +1,24 @@ +using PlanningBook.Domain; +using PlanningBook.Domain.Interfaces; + +namespace PlanningBook.Users.Infrastructure.Entites +{ + public class Person : EntityBase, + IDateAudited, + IAuthorAudited, + IActiveEntity, + ISoftDeleted + { + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public string? PhoneNumber { get; set; } + public DateTime? CreatedDate { get; set; } + public DateTime? UpdatedDate { get; set; } + public Guid? CreatedBy { get; set; } + public Guid? UpdatedBy { get; set; } + public bool IsActive { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletedAt { get; set; } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920030511_Initial_Person_Database.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920030511_Initial_Person_Database.Designer.cs new file mode 100644 index 00000000..0f5b3ccb --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920030511_Initial_Person_Database.Designer.cs @@ -0,0 +1,57 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Users.Infrastructure; + +#nullable disable + +namespace PlanningBook.Users.Infrastructure.Migrations +{ + [DbContext(typeof(PBPersonDbContext))] + [Migration("20240920030511_Initial_Person_Database")] + partial class Initial_Person_Database + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Users.Infrastructure.Entites.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Persons", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920030511_Initial_Person_Database.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920030511_Initial_Person_Database.cs new file mode 100644 index 00000000..3ad8408a --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920030511_Initial_Person_Database.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Users.Infrastructure.Migrations +{ + /// + public partial class Initial_Person_Database : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Persons", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + FirstName = table.Column(type: "nvarchar(max)", nullable: false), + LastName = table.Column(type: "nvarchar(max)", nullable: false), + Email = table.Column(type: "nvarchar(max)", nullable: false), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Persons", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Persons"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920035257_Update_Person_Table_Schema.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920035257_Update_Person_Table_Schema.Designer.cs new file mode 100644 index 00000000..68c0558c --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920035257_Update_Person_Table_Schema.Designer.cs @@ -0,0 +1,78 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Users.Infrastructure; + +#nullable disable + +namespace PlanningBook.Users.Infrastructure.Migrations +{ + [DbContext(typeof(PBPersonDbContext))] + [Migration("20240920035257_Update_Person_Table_Schema")] + partial class Update_Person_Table_Schema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Users.Infrastructure.Entites.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Persons", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920035257_Update_Person_Table_Schema.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920035257_Update_Person_Table_Schema.cs new file mode 100644 index 00000000..fb900340 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20240920035257_Update_Person_Table_Schema.cs @@ -0,0 +1,91 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Users.Infrastructure.Migrations +{ + /// + public partial class Update_Person_Table_Schema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "Persons", + type: "uniqueidentifier", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedDate", + table: "Persons", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletedAt", + table: "Persons", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsActive", + table: "Persons", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "Persons", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + table: "Persons", + type: "uniqueidentifier", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpdatedDate", + table: "Persons", + type: "datetime2", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "Persons"); + + migrationBuilder.DropColumn( + name: "CreatedDate", + table: "Persons"); + + migrationBuilder.DropColumn( + name: "DeletedAt", + table: "Persons"); + + migrationBuilder.DropColumn( + name: "IsActive", + table: "Persons"); + + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "Persons"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + table: "Persons"); + + migrationBuilder.DropColumn( + name: "UpdatedDate", + table: "Persons"); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20241004095554_Update_Person_Table.Designer.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20241004095554_Update_Person_Table.Designer.cs new file mode 100644 index 00000000..b730cf54 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20241004095554_Update_Person_Table.Designer.cs @@ -0,0 +1,74 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Users.Infrastructure; + +#nullable disable + +namespace PlanningBook.Users.Infrastructure.Migrations +{ + [DbContext(typeof(PBPersonDbContext))] + [Migration("20241004095554_Update_Person_Table")] + partial class Update_Person_Table + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Users.Infrastructure.Entites.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Persons", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20241004095554_Update_Person_Table.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20241004095554_Update_Person_Table.cs new file mode 100644 index 00000000..b22d7a76 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/20241004095554_Update_Person_Table.cs @@ -0,0 +1,90 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlanningBook.Users.Infrastructure.Migrations +{ + /// + public partial class Update_Person_Table : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "PhoneNumber", + table: "Persons", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Persons", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "Persons", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Persons", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "PhoneNumber", + table: "Persons", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Persons", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "Persons", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Persons", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/PBPersonDbContextModelSnapshot.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/PBPersonDbContextModelSnapshot.cs new file mode 100644 index 00000000..4418dd17 --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Migrations/PBPersonDbContextModelSnapshot.cs @@ -0,0 +1,71 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlanningBook.Users.Infrastructure; + +#nullable disable + +namespace PlanningBook.Users.Infrastructure.Migrations +{ + [DbContext(typeof(PBPersonDbContext))] + partial class PBPersonDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PlanningBook.Users.Infrastructure.Entites.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Persons", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PBPersonDbContext.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PBPersonDbContext.cs new file mode 100644 index 00000000..bebc9e2d --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PBPersonDbContext.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using System.Reflection; + + +namespace PlanningBook.Users.Infrastructure +{ + public class PBPersonDbContext : DbContext + { + public PBPersonDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PBPersonDbContextFactory.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PBPersonDbContextFactory.cs new file mode 100644 index 00000000..c010f15f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PBPersonDbContextFactory.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using PlanningBook.DBEngine.Constants; + +namespace PlanningBook.Users.Infrastructure +{ + public class PBPersonDbContextFactory : IDesignTimeDbContextFactory + { + public PBPersonDbContext CreateDbContext(string[] args) + { + // Load configuration + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddJsonFile("appsettings.Development.json", optional: true) + .AddEnvironmentVariables() + .Build(); + + var configurationPath = $"{DBEngineConstants.RootConnectionString}:Person{DBEngineConstants.dbConnectionStringPrefix}"; + var connectionString = (args != null && args.Length > 0 && !string.IsNullOrEmpty(args[0])) + ? args[0] + : configuration[configurationPath]; + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(connectionString); + return new PBPersonDbContext(optionsBuilder.Options); + } + } +} diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PlanningBook.Users.Infrastructure.csproj b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PlanningBook.Users.Infrastructure.csproj new file mode 100644 index 00000000..a12bc53f --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/PlanningBook.Users.Infrastructure.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Startup.cs b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Startup.cs new file mode 100644 index 00000000..807777ee --- /dev/null +++ b/.Net/Payment/Stripe - Phat.Tran/Servers/Services/Users/PlanningBook.Users.Infrastructure/Startup.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using PlanningBook.DBEngine.Constants; + +namespace PlanningBook.Users.Infrastructure +{ + public static class Startup + { + public static void AddPBPersonDbContext(this IServiceCollection services, IConfiguration configuration) + { + //TODO-Improve: Use DBEngine in BuildingBlock for add db connection + var test = $"{DBEngineConstants.RootConnectionString}:Person{DBEngineConstants.dbConnectionStringPrefix}"; + services.AddDbContext(options => options.UseSqlServer(configuration[test])); + } + } +}