コード例 #1
0
        public async Task DeleteAsync(string slug, CancellationToken cancellationToken = default)
        {
            slug = slug?.Trim().ToLowerInvariant();

            if (string.IsNullOrWhiteSpace(slug))
            {
                return;
            }

            var fileEntity =
                await _fileRepo
                .Get(x => x.Slug == slug)
                .FirstOrDefaultAsync(cancellationToken: cancellationToken)
                .ConfigureAwait(true);

            if (fileEntity != null)
            {
                cancellationToken.ThrowIfCancellationRequested();

                _fileRepo.Delete(fileEntity, true);

                // Delete Physical File

                var filePath = Path.Combine(Directory.GetCurrentDirectory(), SystemSetting.Current.ResourceFolderPath, fileEntity.Slug);

                FileHelper.SafeDelete(filePath);

                // Save Change

                await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
            }
        }
コード例 #2
0
        protected async Task <SourceEntity> GetSourceEntity(CancellationToken cancellationToken)
        {
            Domain = Domain.Trim().Trim('/').ToLowerInvariant();

            var sourceEntity = await _sourceRepo
                               .Get(x => x.Url == Domain)
                               .FirstOrDefaultAsync(cancellationToken)
                               .ConfigureAwait(true);

            if (sourceEntity == null)
            {
                sourceEntity = new SourceEntity
                {
                    Name               = Name,
                    Url                = Domain,
                    TimeSpent          = TimeSpan.Zero,
                    LastCrawledPostUrl = null
                };

                _sourceRepo.Add(sourceEntity);

                await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
            }

            StopAtPostUrl =
                sourceEntity.LastCrawledPostUrl
                ?.Replace("http://", string.Empty)
                .Replace("https://", string.Empty).Trim('/');

            return(sourceEntity);
        }
コード例 #3
0
        public async Task <GoblinIdentityEmailConfirmationModel> RequestConfirmEmailAsync(long id, CancellationToken cancellationToken = default)
        {
            var userEntity = await _userRepo.Get(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(true);

            if (userEntity == null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.UserNotFound), GoblinIdentityErrorCode.UserNotFound);
            }

            if (userEntity.EmailConfirmedTime != null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.EmailAlreadyConfirmed), GoblinIdentityErrorCode.EmailAlreadyConfirmed);
            }

            var resetConfirmEmailTokenModel = new GoblinIdentityEmailConfirmationModel
            {
                Id = id,
                EmailConfirmToken           = userEntity.EmailConfirmToken = StringHelper.Generate(6, false, false),
                EmailConfirmTokenExpireTime = userEntity.EmailConfirmTokenExpireTime = GoblinDateTimeHelper.SystemTimeNow.Add(SystemSetting.Current.EmailConfirmTokenLifetime)
            };

            _userRepo.Update(userEntity,
                             x => x.EmailConfirmToken,
                             x => x.EmailConfirmTokenExpireTime
                             );

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            return(resetConfirmEmailTokenModel);
        }
コード例 #4
0
        public async Task <GoblinIdentityResetPasswordTokenModel> RequestResetPasswordAsync(
            GoblinIdentityRequestResetPasswordModel model,
            CancellationToken cancellationToken = default)
        {
            var userEntity = await _userRepo.Get(x => x.Email == model.Email && x.EmailConfirmedTime != null)
                             .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(true);

            if (userEntity == null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.UserNotFound),
                                          GoblinIdentityErrorCode.UserNotFound);
            }

            var resetPasswordTokenModel = new GoblinIdentityResetPasswordTokenModel
            {
                SetPasswordToken           = userEntity.SetPasswordToken = StringHelper.Generate(6, false, false),
                SetPasswordTokenExpireTime = userEntity.SetPasswordTokenExpireTime = GoblinDateTimeHelper.SystemTimeNow.Add(SystemSetting.Current.SetPasswordTokenLifetime)
            };

            _userRepo.Update(userEntity,
                             x => x.SetPasswordToken,
                             x => x.SetPasswordTokenExpireTime
                             );

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            return(resetPasswordTokenModel);
        }
コード例 #5
0
        public async Task UpdateProfileAsync(long id, GoblinIdentityUpdateProfileModel model,
                                             CancellationToken cancellationToken = default)
        {
            var userEntity = await _userRepo.Get(x => x.Id == id)
                             .FirstOrDefaultAsync(cancellationToken)
                             .ConfigureAwait(true);

            if (userEntity == null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.UserNotFound),
                                          GoblinIdentityErrorCode.UserNotFound);
            }

            using var transaction =
                      await GoblinUnitOfWork.BeginTransactionAsync(cancellationToken).ConfigureAwait(true);

            if (model.IsUpdateRoles)
            {
                _userRoleRepo.DeleteWhere(x => x.UserId == userEntity.Id);

                await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

                // User Roles

                if (model.Roles?.Any() == true)
                {
                    model.Roles = model.Roles.Select(x => x.Trim()).ToList();

                    var roleEntities = await _roleRepo.Get(x => model.Roles.Contains(x.Name))
                                       .ToListAsync(cancellationToken).ConfigureAwait(true);

                    foreach (var roleEntity in roleEntities)
                    {
                        _userRoleRepo.Add(new UserRoleEntity
                        {
                            UserId = userEntity.Id,
                            RoleId = roleEntity.Id
                        });
                    }
                }
            }

            model.MapTo(userEntity);

            _userRepo.Update(userEntity,
                             x => x.AvatarUrl,
                             x => x.FullName,
                             x => x.Bio,
                             x => x.GithubId,
                             x => x.SkypeId,
                             x => x.FacebookId,
                             x => x.WebsiteUrl,
                             x => x.CompanyName,
                             x => x.CompanyUrl
                             );

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            transaction.Commit();
        }
コード例 #6
0
        public async Task ResetPasswordAsync(GoblinIdentityResetPasswordModel model,
                                             CancellationToken cancellationToken = default)
        {
            var userEntity = await _userRepo.Get(x => x.Email == model.Email && x.EmailConfirmedTime != null)
                             .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(true);

            if (userEntity == null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.UserNotFound),
                                          GoblinIdentityErrorCode.UserNotFound);
            }

            if (userEntity.SetPasswordToken == model.SetPasswordToken)
            {
                if (userEntity.SetPasswordTokenExpireTime < GoblinDateTimeHelper.SystemTimeNow)
                {
                    throw new GoblinException(nameof(GoblinIdentityErrorCode.SetPasswordTokenExpired),
                                              GoblinIdentityErrorCode.SetPasswordTokenExpired);
                }
            }
            else
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.SetPasswordTokenInCorrect),
                                          GoblinIdentityErrorCode.SetPasswordTokenInCorrect);
            }

            var changedProperties = new List <string>();

            var newPasswordHashWithOldSalt =
                PasswordHelper.HashPassword(model.NewPassword, userEntity.PasswordLastUpdatedTime);

            // If user have changed password, then update password and related information
            if (newPasswordHashWithOldSalt != userEntity.PasswordHash)
            {
                userEntity.PasswordLastUpdatedTime            =
                    userEntity.RevokeTokenGeneratedBeforeTime = GoblinDateTimeHelper.SystemTimeNow;
                changedProperties.Add(nameof(userEntity.PasswordLastUpdatedTime));
                changedProperties.Add(nameof(userEntity.RevokeTokenGeneratedBeforeTime));

                userEntity.PasswordHash =
                    PasswordHelper.HashPassword(model.NewPassword, userEntity.PasswordLastUpdatedTime);
                changedProperties.Add(nameof(userEntity.PasswordHash));
            }

            userEntity.SetPasswordToken = null;
            changedProperties.Add(nameof(userEntity.SetPasswordToken));

            userEntity.SetPasswordTokenExpireTime = null;
            changedProperties.Add(nameof(userEntity.SetPasswordTokenExpireTime));

            _userRepo.Update(userEntity, changedProperties.ToArray());

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
        }
コード例 #7
0
        public async Task DeleteAsync(long id, CancellationToken cancellationToken = default)
        {
            var sampleEntity =
                await _sampleRepo
                .Get(x => x.Id == id)
                .FirstOrDefaultAsync(cancellationToken: cancellationToken)
                .ConfigureAwait(true);

            _sampleRepo.Delete(sampleEntity);

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
        }
コード例 #8
0
        public async Task <GoblinApi_BaseSampleModel> CreateAsync(GoblinApi_BaseCreateSampleModel model,
                                                                  CancellationToken cancellationToken = default)
        {
            var sampleEntity = model.MapTo <SampleEntity>();

            _sampleRepo.Add(sampleEntity);

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            var fileModel = sampleEntity.MapTo <GoblinApi_BaseSampleModel>();

            return(fileModel);
        }
コード例 #9
0
        public virtual async Task CrawlPostsAsync(CancellationToken cancellationToken = default)
        {
            var startTime = GoblinDateTimeHelper.SystemTimeNow;

            var sourceEntity = await GetSourceEntity(cancellationToken);

            var crawledPostUrl = await CrawlPostUrlAsync(cancellationToken).ConfigureAwait(true);

            var postUrls = crawledPostUrl.TakeWhile(url => url != sourceEntity.LastCrawledPostUrl).ToList();

            var postsMetadata = await GoblinCrawlerHelper.GetListMetadataModelsAsync(postUrls).ConfigureAwait(true);

            using var transaction =
                      await GoblinUnitOfWork.BeginTransactionAsync(cancellationToken).ConfigureAwait(true);

            // Posts Metadata to Post Crawled Database

            await GoblinCrawlerHelper.SavePostEntitiesAsync(Domain, postsMetadata, startTime, _postRepo, GoblinUnitOfWork).ConfigureAwait(true);

            // Update Source

            sourceEntity.LastCrawlStartTime       = startTime;
            sourceEntity.LastCrawlEndTime         = GoblinDateTimeHelper.SystemTimeNow;
            sourceEntity.TimeSpent                = sourceEntity.LastCrawlEndTime.Subtract(sourceEntity.LastCrawlStartTime);
            sourceEntity.TotalPostCrawledLastTime = postsMetadata.Count;
            sourceEntity.TotalPostCrawled        += postsMetadata.Count;

            if (!string.IsNullOrWhiteSpace(postsMetadata.FirstOrDefault()?.OriginalUrl))
            {
                sourceEntity.LastCrawledPostUrl = postsMetadata.FirstOrDefault()?.OriginalUrl;
            }

            _sourceRepo.Update(sourceEntity,
                               x => x.LastCrawlStartTime,
                               x => x.LastCrawlEndTime,
                               x => x.TimeSpent,
                               x => x.TotalPostCrawledLastTime,
                               x => x.TotalPostCrawled,
                               x => x.LastCrawledPostUrl
                               );

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            transaction.Commit();
        }
コード例 #10
0
        public async Task DeleteAsync(long id, CancellationToken cancellationToken = default)
        {
            var userEntity =
                await _userRepo
                .Get(x => x.Id == id)
                .FirstOrDefaultAsync(cancellationToken: cancellationToken)
                .ConfigureAwait(true);

            if (userEntity == null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.UserNotFound),
                                          GoblinIdentityErrorCode.UserNotFound);
            }

            _userRepo.Delete(userEntity);

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
        }
コード例 #11
0
        public async Task ConfirmEmail(long id, GoblinIdentityConfirmEmailModel model,
                                       CancellationToken cancellationToken = default)
        {
            var userEntity = await _userRepo.Get(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(true);

            if (userEntity == null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.UserNotFound),
                                          GoblinIdentityErrorCode.UserNotFound);
            }

            if (userEntity.EmailConfirmToken == model.EmailConfirmToken)
            {
                if (userEntity.EmailConfirmTokenExpireTime < GoblinDateTimeHelper.SystemTimeNow)
                {
                    throw new GoblinException(nameof(GoblinIdentityErrorCode.ConfirmEmailTokenExpired),
                                              GoblinIdentityErrorCode.ConfirmEmailTokenExpired);
                }
            }
            else
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.ConfirmEmailTokenInCorrect),
                                          GoblinIdentityErrorCode.ConfirmEmailTokenInCorrect);
            }

            userEntity.EmailConfirmToken           = null;
            userEntity.EmailConfirmTokenExpireTime = null;
            userEntity.EmailConfirmedTime          = GoblinDateTimeHelper.SystemTimeNow;

            _userRepo.Update(userEntity,
                             x => x.EmailConfirmToken,
                             x => x.EmailConfirmTokenExpireTime,
                             x => x.EmailConfirmedTime
                             );

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
        }
コード例 #12
0
        public async Task <GoblinResourceFileModel> SaveAsync(GoblinResourceUploadFileModel model, CancellationToken cancellationToken = default)
        {
            FileServiceHelper.Correct(model);

            var fileEntity = model.MapTo <FileEntity>();

            fileEntity.Hash = SecurityHelper.EncryptSha256(model.ContentBase64);

            var fileBytes = Convert.FromBase64String(model.ContentBase64);

            var imageInfo = ImageHelper.GetImageInfo(fileBytes);

            // Fill information based on File is Image or not

            FileServiceHelper.FillInformation(imageInfo, fileEntity);

            cancellationToken.ThrowIfCancellationRequested();

            string fileName;

            // Save Files

            if (imageInfo != null)
            {
                // Main Image

                var isNeedResizeImage = model.ImageMaxHeightPx < fileEntity.ImageHeightPx ||
                                        model.ImageMaxWidthPx < fileEntity.ImageWidthPx;

                fileBytes = FileServiceHelper.ResizeAndCompressImage(fileBytes,
                                                                     isNeedResizeImage,
                                                                     model.ImageMaxWidthPx.Value,
                                                                     model.ImageMaxHeightPx.Value,
                                                                     model.IsEnableCompressImage);

                if (model.IsEnableCompressImage)
                {
                    fileEntity.IsCompressedImage = true;
                }

                // Refill information after resize and compress

                if (isNeedResizeImage || model.IsEnableCompressImage)
                {
                    var newImageInfo = ImageHelper.GetImageInfo(fileBytes);
                    FileServiceHelper.FillInformation(newImageInfo, fileEntity);
                }

                // Save File

                fileName = FileServiceHelper.GenerateFileName(fileEntity.Name, imageInfo);

                fileEntity.Slug = FileServiceHelper.SaveFile(fileBytes, model.Folder, fileName, string.Empty, fileEntity.Extension);

                fileEntity.ContentLength = fileBytes.Length;

                // --------------------------
                //    Save More Image Size
                // --------------------------

                // Image Skeleton

                var imageSkeletonBytes = FileServiceHelper.ResizeAndCompressImage(fileBytes,
                                                                                  true,
                                                                                  SystemSetting.Current.ImageSkeletonMaxWidthPx,
                                                                                  SystemSetting.Current.ImageSkeletonMaxHeightPx,
                                                                                  true);

                FileServiceHelper.SaveFile(imageSkeletonBytes, model.Folder, fileName, "-s", fileEntity.Extension);

                // Image Thumbnail

                var imageThumbnailBytes = FileServiceHelper.ResizeAndCompressImage(fileBytes,
                                                                                   true,
                                                                                   SystemSetting.Current.ImageThumbnailMaxWidthPx,
                                                                                   SystemSetting.Current.ImageThumbnailMaxHeightPx,
                                                                                   true);

                FileServiceHelper.SaveFile(imageThumbnailBytes, model.Folder, fileName, "-t", fileEntity.Extension);
            }
            else
            {
                fileName = FileServiceHelper.GenerateFileName(fileEntity.Name, null);

                fileEntity.Slug = FileServiceHelper.SaveFile(fileBytes, model.Folder, fileName, string.Empty, fileEntity.Extension);

                fileEntity.ContentLength = fileBytes.Length;
            }

            _fileRepo.Add(fileEntity);

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            // Response

            var fileModel = fileEntity.MapTo <GoblinResourceFileModel>();

            return(fileModel);
        }
コード例 #13
0
        public async Task <GoblinIdentityRoleModel> UpsertAsync(GoblinIdentityUpsertRoleModel model,
                                                                CancellationToken cancellationToken = default)
        {
            using var transaction =
                      await GoblinUnitOfWork.BeginTransactionAsync(cancellationToken).ConfigureAwait(true);

            // Handle Role

            var roleEntity = await _roleRepo
                             .Get(x => x.Name == model.Name)
                             .FirstOrDefaultAsync(cancellationToken)
                             .ConfigureAwait(true);

            if (roleEntity == null)
            {
                roleEntity = new RoleEntity
                {
                    Name = model.Name
                };

                _roleRepo.Add(roleEntity);
            }

            // Save Change
            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken);

            // Handle Permission

            if (model.Permissions?.Any() == true)
            {
                model.Permissions = model.Permissions.Select(x => x.Trim()).ToList();

                var existsPermissionEntities =
                    await _permissionRepo
                    .Get(x => model.Permissions.Contains(x.Name))
                    .ToListAsync(cancellationToken).ConfigureAwait(true);

                var existsPermissions = existsPermissionEntities.Select(x => x.Name).ToList();

                foreach (var permission in model.Permissions)
                {
                    if (!existsPermissions.Contains(permission))
                    {
                        var permissionEntity = new PermissionEntity
                        {
                            Name = permission
                        };

                        _permissionRepo.Add(permissionEntity);

                        existsPermissionEntities.Add(permissionEntity);
                    }
                }

                // Save Change
                await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

                // Relationship Role and Permission

                var rolePermissions = await _rolePermissionRepo.Get(x => x.RoleId == roleEntity.Id)
                                      .ToListAsync(cancellationToken)
                                      .ConfigureAwait(true);

                foreach (var permissionEntity in existsPermissionEntities)
                {
                    if (rolePermissions.Any(x => x.PermissionId == permissionEntity.Id))
                    {
                        continue;
                    }

                    _rolePermissionRepo.Add(new RolePermissionEntity
                    {
                        RoleId       = roleEntity.Id,
                        PermissionId = permissionEntity.Id
                    });
                }

                // Save Change
                await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
            }

            transaction.Commit();

            var roleModel = await GetAsync(roleEntity.Name, cancellationToken).ConfigureAwait(true);

            return(roleModel);
        }
コード例 #14
0
        public async Task <GoblinIdentityEmailConfirmationModel> UpdateIdentityAsync(long id,
                                                                                     GoblinIdentityUpdateIdentityModel model,
                                                                                     CancellationToken cancellationToken = default)
        {
            model.NewEmail = model.NewEmail?.Trim().ToLowerInvariant();

            model.NewUserName = model.NewUserName?.Trim().ToLowerInvariant();

            var userEntity = await _userRepo.Get(x => x.Id == id)
                             .FirstOrDefaultAsync(cancellationToken)
                             .ConfigureAwait(true);

            if (userEntity == null)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.UserNotFound), GoblinIdentityErrorCode.UserNotFound);
            }

            var currentPasswordHashWithOldSalt = PasswordHelper.HashPassword(model.CurrentPassword, userEntity.PasswordLastUpdatedTime);

            if (currentPasswordHashWithOldSalt != userEntity.PasswordHash)
            {
                throw new GoblinException(nameof(GoblinIdentityErrorCode.WrongPassword), GoblinIdentityErrorCode.WrongPassword);
            }

            var emailConfirmationModel = new GoblinIdentityEmailConfirmationModel
            {
                Id = userEntity.Id
            };

            var changedProperties = new List <string>();

            // Update Password
            if (!string.IsNullOrWhiteSpace(model.NewPassword))
            {
                var newPasswordHashWithOldSalt = PasswordHelper.HashPassword(model.NewPassword, userEntity.PasswordLastUpdatedTime);

                // If user have changed password, then update password and related information
                if (newPasswordHashWithOldSalt != userEntity.PasswordHash)
                {
                    userEntity.PasswordLastUpdatedTime            =
                        userEntity.RevokeTokenGeneratedBeforeTime = GoblinDateTimeHelper.SystemTimeNow;
                    changedProperties.Add(nameof(userEntity.PasswordLastUpdatedTime));
                    changedProperties.Add(nameof(userEntity.RevokeTokenGeneratedBeforeTime));

                    userEntity.PasswordHash =
                        PasswordHelper.HashPassword(model.NewPassword, userEntity.PasswordLastUpdatedTime);
                    changedProperties.Add(nameof(userEntity.PasswordHash));
                }
            }

            // Update Email
            if (!string.IsNullOrWhiteSpace(model.NewEmail) && model.NewEmail != userEntity.Email)
            {
                CheckUniqueEmail(model.NewEmail);

                userEntity.EmailConfirmToken = StringHelper.Generate(6, false, false);
                changedProperties.Add(nameof(userEntity.EmailConfirmToken));

                userEntity.EmailConfirmTokenExpireTime = GoblinDateTimeHelper.SystemTimeNow.Add(SystemSetting.Current.EmailConfirmTokenLifetime);
                changedProperties.Add(nameof(userEntity.EmailConfirmTokenExpireTime));


                // Email Confirmation Token

                emailConfirmationModel.EmailConfirmToken = userEntity.EmailConfirmToken;

                emailConfirmationModel.EmailConfirmTokenExpireTime = userEntity.EmailConfirmTokenExpireTime;
            }

            // Update UserName
            if (!string.IsNullOrWhiteSpace(model.NewUserName))
            {
                CheckUniqueUserName(model.NewUserName);

                userEntity.UserName = model.NewUserName;
                changedProperties.Add(nameof(userEntity.UserName));
            }

            if (!changedProperties.Any())
            {
                return(emailConfirmationModel);
            }

            _userRepo.Update(userEntity, changedProperties.ToArray());

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            return(emailConfirmationModel);
        }
コード例 #15
0
        public async Task <GoblinIdentityEmailConfirmationModel> RegisterAsync(GoblinIdentityRegisterModel model,
                                                                               CancellationToken cancellationToken = default)
        {
            model.Email = model.Email?.Trim().ToLowerInvariant();

            model.UserName = model.UserName?.Trim().ToLowerInvariant();

            CheckUniqueEmail(model.Email);

            CheckUniqueUserName(model.UserName);

            using var transaction =
                      await GoblinUnitOfWork.BeginTransactionAsync(cancellationToken).ConfigureAwait(true);

            var userEntity = model.MapTo <UserEntity>();

            userEntity.PasswordLastUpdatedTime            =
                userEntity.RevokeTokenGeneratedBeforeTime = GoblinDateTimeHelper.SystemTimeNow;

            userEntity.PasswordHash =
                PasswordHelper.HashPassword(model.Password, userEntity.PasswordLastUpdatedTime);

            userEntity.EmailConfirmToken = StringHelper.Generate(6, false, false);

            userEntity.EmailConfirmTokenExpireTime =
                GoblinDateTimeHelper.SystemTimeNow.Add(SystemSetting.Current.EmailConfirmTokenLifetime);

            _userRepo.Add(userEntity);

            await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);

            // User Roles

            if (model.Roles?.Any() == true)
            {
                model.Roles = model.Roles.Select(x => x.Trim()).ToList();

                var roleEntities = await _roleRepo.Get(x => model.Roles.Contains(x.Name)).ToListAsync(cancellationToken)
                                   .ConfigureAwait(true);

                foreach (var roleEntity in roleEntities)
                {
                    _userRoleRepo.Add(new UserRoleEntity
                    {
                        UserId = userEntity.Id,
                        RoleId = roleEntity.Id
                    });
                }

                await GoblinUnitOfWork.SaveChangesAsync(cancellationToken).ConfigureAwait(true);
            }

            transaction.Commit();

            // Email Confirmation Code

            var emailConfirmationModel = new GoblinIdentityEmailConfirmationModel
            {
                Id = userEntity.Id,
                EmailConfirmToken           = userEntity.EmailConfirmToken,
                EmailConfirmTokenExpireTime = userEntity.EmailConfirmTokenExpireTime
            };

            return(emailConfirmationModel);
        }