Skip to content

Commit

Permalink
Upgrade to v8.1
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed Feb 6, 2024
1 parent 252767c commit c986435
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 118 deletions.
4 changes: 3 additions & 1 deletion MyApp.ServiceInterface/EmailServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ public class SmtpConfig
/// <summary>
/// Uses a configured SMTP client to send emails
/// </summary>
public class EmailServices(SmtpConfig config, ILogger<EmailServices> log) : Service
public class EmailServices(SmtpConfig config, ILogger<EmailServices> log)
// TODO: Uncomment to enable sending emails with SMTP
// : Service
{
public object Any(SendEmail request)
{
Expand Down
5 changes: 3 additions & 2 deletions MyApp/Configure.Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ public class ConfigureAuth : IHostingStartup
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services => {
services.AddSingleton<IAuthHttpGateway, AuthHttpGateway>();
services.AddTransient<IExternalLoginAuthInfoProvider,ExternalLoginAuthInfoProvider>();
services.AddTransient<IExternalLoginAuthInfoProvider, ExternalLoginAuthInfoProvider>();
})
.ConfigureAppHost(appHost =>
{
appHost.Plugins.Add(new AuthFeature(IdentityAuth.For<ApplicationUser>(options => {
options.EnableCredentialsAuth = true;
options.SessionFactory = () => new CustomUserSession();
options.CredentialsAuth();
options.AdminUsersFeature();
})));
});
}
Expand Down
12 changes: 7 additions & 5 deletions MyApp/Configure.AutoQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services => {
// Enable Audit History
services.AddSingleton<ICrudEvents>(c =>
new OrmLiteCrudEvents(c.Resolve<IDbConnectionFactory>()));
})
.ConfigureAppHost(appHost => {
new OrmLiteCrudEvents(c.GetRequiredService<IDbConnectionFactory>()));
// For TodosService
services.AddPlugin(new AutoQueryDataFeature());
// For Bookings https://docs.servicestack.net/autoquery-crud-bookings
appHost.Plugins.Add(new AutoQueryFeature
services.AddPlugin(new AutoQueryFeature
{
MaxLimit = 1000,
//IncludeTotal = true,
});
})
.ConfigureAppHost(appHost => {
appHost.Resolve<ICrudEvents>().InitSchema();
});
}
7 changes: 3 additions & 4 deletions MyApp/Configure.Db.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ public void Configure(IWebHostBuilder builder) => builder
context.Configuration.GetConnectionString("DefaultConnection")
?? ":memory:",
SqliteDialect.Provider));
})
.ConfigureAppHost(appHost => {
// Enable built-in Database Admin UI at /admin-ui/database
appHost.Plugins.Add(new AdminDatabaseFeature());
services.AddPlugin(new AdminDatabaseFeature());
});
}
}
2 changes: 0 additions & 2 deletions MyApp/Configure.Markdown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ public void Configure(IWebHostBuilder builder) => builder
var blogPosts = appHost.Resolve<MarkdownBlog>();
blogPosts.Authors = AppConfig.Instance.Authors;
new IMarkdownPages[] { pages, blogPosts }
.Each(x => x.VirtualFiles = appHost.VirtualFiles);
pages.LoadFrom("_pages");
blogPosts.LoadFrom("_posts");
Expand Down
45 changes: 27 additions & 18 deletions MyApp/Configure.Mq.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Text.Encodings.Web;
using ServiceStack;
using ServiceStack.Messaging;
using MyApp.Data;
using MyApp.ServiceInterface;
using MyApp.ServiceModel;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using MyApp.Data;
using ServiceStack;

[assembly: HostingStartup(typeof(MyApp.ConfigureMq))]

Expand All @@ -24,34 +26,33 @@ public void Configure(IWebHostBuilder builder) => builder
services.AddSingleton<IMessageService>(c => new BackgroundMqService());
})
.ConfigureAppHost(afterAppHostInit: appHost => {
appHost.Resolve<IMessageService>().Start();
var mqService = appHost.Resolve<IMessageService>();
//Register ServiceStack APIs you want to be able to invoke via MQ
mqService.RegisterHandler<SendEmail>(appHost.ExecuteMessage);
mqService.Start();
});
}

// This class is used by the application to send email for account confirmation and password reset.
// For more details see https://go.microsoft.com/fwlink/?LinkID=532713
public interface IEmailSender
// Remove the "else if (EmailSender is IdentityNoOpEmailSender)" block from RegisterConfirmation.razor after updating with a real implementation.
internal sealed class IdentityNoOpEmailSender : IEmailSender<ApplicationUser>
{
Task SendEmailAsync(string email, string subject, string message);
}
private readonly IEmailSender emailSender = new NoOpEmailSender();

public static class EmailSenderExtensions
{
public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link)
{
return emailSender.SendEmailAsync(email, "Confirm your email",
$"Please confirm your account by clicking this link: <a href='{HtmlEncoder.Default.Encode(link)}'>link</a>");
}
public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) =>
emailSender.SendEmailAsync(email, "Confirm your email", $"Please confirm your account by <a href='{confirmationLink}'>clicking here</a>.");

public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) =>
emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");

public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) =>
emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password using the following code: {resetCode}");
}

/// <summary>
/// Sends emails by publishing a message to the Background MQ Server where it's processed in the background
/// </summary>
public class EmailSender(IMessageService messageService) : IEmailSender
public class EmailSender(IMessageService messageService) : IEmailSender<ApplicationUser>
{
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
Expand All @@ -65,5 +66,13 @@ public Task SendEmailAsync(string email, string subject, string htmlMessage)

return Task.CompletedTask;
}
}

public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) =>
SendEmailAsync(email, "Confirm your email", $"Please confirm your account by <a href='{confirmationLink}'>clicking here</a>.");

public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) =>
SendEmailAsync(email, "Reset your password", $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");

public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) =>
SendEmailAsync(email, "Reset your password", $"Please reset your password using the following code: {resetCode}");
}
9 changes: 4 additions & 5 deletions MyApp/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ public class AccountController : ServiceStackController
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private IExternalLoginAuthInfoProvider _authInfoProvider;
private readonly IEmailSender _emailSender;
private readonly IEmailSender<ApplicationUser> _emailSender;
private readonly ILogger _logger;

public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IExternalLoginAuthInfoProvider authInfoProvider,
IEmailSender emailSender,
IEmailSender<ApplicationUser> emailSender,
ILogger<AccountController> logger)
{
_userManager = userManager;
Expand Down Expand Up @@ -231,7 +231,7 @@ public async Task<IActionResult> Register(RegisterViewModel model, string return

var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
await _emailSender.SendConfirmationLinkAsync(user, model.Email, callbackUrl);

await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created a new account with password.");
Expand Down Expand Up @@ -382,8 +382,7 @@ public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
// visit https://go.microsoft.com/fwlink/?LinkID=532713
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.ResetPasswordCallbackLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Reset Password",
$"Please reset your password by clicking here: <a href='{callbackUrl}'>link</a>");
await _emailSender.SendPasswordResetLinkAsync(user, model.Email, callbackUrl);
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}

Expand Down
6 changes: 3 additions & 3 deletions MyApp/Controllers/ManageController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class ManageController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly IEmailSender<ApplicationUser> _emailSender;
private readonly ILogger _logger;
private readonly UrlEncoder _urlEncoder;

Expand All @@ -26,7 +26,7 @@ public class ManageController : Controller
public ManageController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
IEmailSender<ApplicationUser> emailSender,
ILogger<ManageController> logger,
UrlEncoder urlEncoder)
{
Expand Down Expand Up @@ -119,7 +119,7 @@ public async Task<IActionResult> SendVerificationEmail(IndexViewModel model)
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
var email = user.Email;
await _emailSender.SendEmailConfirmationAsync(email, callbackUrl);
await _emailSender.SendConfirmationLinkAsync(user, email, callbackUrl);

StatusMessage = "Verification email sent. Please check your email.";
return RedirectToAction(nameof(Index));
Expand Down
52 changes: 24 additions & 28 deletions MyApp/Markdown.Blog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Markdig;
using ServiceStack;
using ServiceStack.IO;
using ServiceStack.Logging;

namespace MyApp;

Expand All @@ -19,18 +18,18 @@ public class AuthorInfo
public string? MastodonUrl { get; set; }
}

public class MarkdownBlog : MarkdownPagesBase<MarkdownFileInfo>
public class MarkdownBlog(ILogger<MarkdownBlog> log, IWebHostEnvironment env, IVirtualFiles fs)
: MarkdownPagesBase<MarkdownFileInfo>(log, env, fs)
{
public override string Id => "posts";
public MarkdownBlog(ILogger<MarkdownBlog> log, IWebHostEnvironment env) : base(log,env) {}
List<MarkdownFileInfo> Posts { get; set; } = new();
List<MarkdownFileInfo> Posts { get; set; } = [];

public List<MarkdownFileInfo> VisiblePosts => Posts.Where(IsVisible).ToList();

public string FallbackProfileUrl { get; set; } = Svg.ToDataUri(Svg.Create(Svg.Body.User, stroke:"none").Replace("fill='currentColor'","fill='#0891b2'"));
public string FallbackSplashUrl { get; set; } = "https://source.unsplash.com/random/2000x1000/?stationary";

public List<AuthorInfo> Authors { get; set; } = new();
public List<AuthorInfo> Authors { get; set; } = [];

public Dictionary<string, AuthorInfo> AuthorSlugMap { get; } = new();
public Dictionary<string, string> TagSlugMap { get; } = new();
Expand Down Expand Up @@ -70,26 +69,25 @@ public List<MarkdownFileInfo> GetPosts(string? author = null, string? tag = null
return latestPosts.OrderByDescending(x => x.Date).ToList();
}

public string GetPostLink(MarkdownFileInfo post) => $"/posts/{post.Slug}";
public string GetPostLink(MarkdownFileInfo post) => $"posts/{post.Slug}";

public string GetAllPostsLink() => "/Blog";
public string GetPostsLink() => "posts/";
public string? GetAuthorLink(string? author) => author != null && Authors.Any(x => x.Name.Equals(author, StringComparison.OrdinalIgnoreCase))
? $"posts/author/{author.GenerateSlug()}"
: null;

public string GetYearLink(int year) => $"posts/year/{year}";
public string GetTagLink(string tag) => $"posts/tagged/{tag.GenerateSlug()}";
public string GetDateLabel(DateTime? date) => X.Map(date ?? DateTime.UtcNow, d => d.ToString("MMMM d, yyyy"))!;
public string GetDateTimestamp(DateTime? date) => X.Map(date ?? DateTime.UtcNow, d => d.ToString("O"))!;

public AuthorInfo? GetAuthorBySlug(string? slug)
{
return AuthorSlugMap.TryGetValue(slug, out var author)
? author
: null;
}
public AuthorInfo? GetAuthorBySlug(string? slug) => slug != null && AuthorSlugMap.TryGetValue(slug, out var author)
? author
: null;

public string? GetTagBySlug(string? slug)
{
return TagSlugMap.TryGetValue(slug, out var tag)
? tag
: null;
}
public string? GetTagBySlug(string? slug) => slug != null && TagSlugMap.TryGetValue(slug, out var tag)
? tag
: null;

public string GetSplashImage(MarkdownFileInfo post)
{
Expand All @@ -104,14 +102,14 @@ public string GetSplashImage(MarkdownFileInfo post)
public override MarkdownFileInfo? Load(string path, MarkdownPipeline? pipeline = null)
{
var file = VirtualFiles.GetFile(path)
?? throw new FileNotFoundException(path.LastRightPart('/'));
?? throw new FileNotFoundException(path.LastRightPart('/'));
var content = file.ReadAllText();

var writer = new StringWriter();
var doc = CreateMarkdownFile(content, writer, pipeline);
if (doc?.Title == null)
if (doc.Title == null)
{
Log.LogWarning("No frontmatter found for {0}, ignoring...", file.VirtualPath);
log.LogWarning("No frontmatter found for {VirtualPath}, ignoring...", file.VirtualPath);
return null;
}

Expand All @@ -123,7 +121,7 @@ public string GetSplashImage(MarkdownFileInfo post)
if (!DateTime.TryParseExact(datePart, "yyyy-MM-dd", CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal, out var date))
{
Log.LogWarning("Could not parse date '{0}', ignoring...", datePart);
log.LogWarning("Could not parse date '{DatePart}', ignoring...", datePart);
return null;
}

Expand All @@ -139,10 +137,8 @@ public string GetSplashImage(MarkdownFileInfo post)
public void LoadFrom(string fromDirectory)
{
Posts.Clear();
var fs = AssertVirtualFiles();
var files = fs.GetDirectory(fromDirectory).GetAllFiles().ToList();
var log = LogManager.GetLogger(GetType());
log.InfoFormat("Found {0} posts", files.Count);
var files = VirtualFiles.GetDirectory(fromDirectory).GetAllFiles().ToList();
log.LogInformation("Found {Count} posts", files.Count);

var pipeline = CreatePipeline();

Expand All @@ -158,7 +154,7 @@ public void LoadFrom(string fromDirectory)
}
catch (Exception e)
{
log.Error(e, "Couldn't load {0}: {1}", file.VirtualPath, e.Message);
log.LogError(e, "Couldn't load {VirtualPath}: {Message}", file.VirtualPath, e.Message);
}
}

Expand Down
Loading

0 comments on commit c986435

Please sign in to comment.