Skip to content

Commit

Permalink
Replace MQ with ServiceStack Jobs implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed Sep 4, 2024
1 parent cb0fcd9 commit 0a5943d
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 135 deletions.
10 changes: 0 additions & 10 deletions MyApp.Client/nuget.config

This file was deleted.

66 changes: 33 additions & 33 deletions MyApp.Client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,56 +11,56 @@
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-slot": "^1.0.2",
"@servicestack/client": "^2.1.1",
"@tanstack/react-table": "^8.12.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@servicestack/client": "^2.1.5",
"@tanstack/react-table": "^8.20.5",
"class-variance-authority": "^0.7.0",
"classnames": "^2.5.1",
"clsx": "^2.1.0",
"clsx": "^2.1.1",
"gray-matter": "^4.0.3",
"lucide-react": "^0.331.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"lucide-react": "^0.438.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
"react-helmet-async": "^2.0.4",
"react-router": "^6.22.0",
"react-router-dom": "^6.22.0",
"swr": "^2.2.4",
"tailwind-merge": "^2.2.1",
"react-helmet-async": "^2.0.5",
"react-router": "^6.26.1",
"react-router-dom": "^6.26.1",
"swr": "^2.2.5",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@iconify/react": "^4.1.1",
"@iconify/react": "^5.0.2",
"@mdx-js/rollup": "^3.0.1",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@types/mdx": "^2.0.11",
"@types/node": "^20.11.19",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@tailwindcss/forms": "^0.5.8",
"@tailwindcss/typography": "^0.5.15",
"@types/mdx": "^2.0.13",
"@types/node": "^22.5.3",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11",
"@types/remark-prism": "^1.3.7",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"postcss": "^8.4.35",
"@typescript-eslint/eslint-plugin": "^8.4.0",
"@typescript-eslint/parser": "^8.4.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.1",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.11",
"postcss": "^8.4.45",
"rehype-raw": "^7.0.0",
"rehype-stringify": "^10.0.0",
"remark-directive": "^3.0.0",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.0",
"remark-parse": "^11.0.0",
"remark-prism": "^1.3.6",
"tailwindcss": "^3.4.1",
"typescript": "^5.2.2",
"vite": "^5.1.0",
"vite-plugin-pages": "^0.32.0",
"tailwindcss": "^3.4.10",
"typescript": "^5.5.4",
"vite": "^5.4.3",
"vite-plugin-pages": "^0.32.3",
"vite-plugin-press": "^1.0.10",
"vite-plugin-svgr": "^4.2.0"
}
Expand Down
17 changes: 17 additions & 0 deletions MyApp.Client/src/pages/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useSearchParams } from "react-router-dom"

export default () => {
const [query, _] = useSearchParams()
const message = query.get('message') ?? 'Unknown error'

return (<>
<div className="text-black bg-white h-screen text-center flex flex-col items-center justify-center">
<div>
<div className="inline-block text-left h-8 align-middle">
<h2 className="text-2xl text-red-700 leading-10 font-normal m-0 p-0">{message}</h2>
</div>
</div>
</div>
</>
)
}
30 changes: 19 additions & 11 deletions MyApp.ServiceInterface/EmailServices.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Net.Mail;
using Microsoft.Extensions.Logging;
using ServiceStack;
using MyApp.ServiceModel;
using ServiceStack.Jobs;

namespace MyApp.ServiceInterface;

Expand Down Expand Up @@ -45,16 +45,26 @@ public class SmtpConfig
public string? Bcc { get; set; }
}

/// <summary>
/// Uses a configured SMTP client to send emails
/// </summary>
public class EmailServices(SmtpConfig config, ILogger<EmailServices> log)
// TODO: Uncomment to enable sending emails with SMTP
// : Service
public class SendEmail
{
public string To { get; set; }
public string? ToName { get; set; }
public string Subject { get; set; }
public string? BodyText { get; set; }
public string? BodyHtml { get; set; }
}

[Worker("smtp")]
public class SendEmailCommand(ILogger<SendEmailCommand> logger, IBackgroundJobs jobs, SmtpConfig config)
: SyncCommand<SendEmail>
{
public object Any(SendEmail request)
private static long count = 0;
protected override void Run(SendEmail request)
{
log.LogInformation("Sending email to {Email} with subject {Subject}", request.To, request.Subject);
Interlocked.Increment(ref count);
var log = Request.CreateJobLogger(jobs, logger);
log.LogInformation("Sending {Count} email to {Email} with subject {Subject}",
count, request.To, request.Subject);

using var client = new SmtpClient(config.Host, config.Port);
client.Credentials = new System.Net.NetworkCredential(config.Username, config.Password);
Expand All @@ -80,7 +90,5 @@ public object Any(SendEmail request)
}

client.Send(msg);

return new EmptyResponse();
}
}
15 changes: 0 additions & 15 deletions MyApp.ServiceModel/Emails.cs

This file was deleted.

62 changes: 62 additions & 0 deletions MyApp/Configure.BackgroundJobs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Identity;
using MyApp.Data;
using MyApp.ServiceInterface;
using ServiceStack.Jobs;

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

namespace MyApp;

public class ConfigureBackgroundJobs : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services => {
services.AddPlugin(new CommandsFeature());
services.AddPlugin(new BackgroundsJobFeature());
services.AddHostedService<JobsHostedService>();
}).ConfigureAppHost(afterAppHostInit: appHost => {
var services = appHost.GetApplicationServices();
var jobs = services.GetRequiredService<IBackgroundJobs>();
// Example of registering a Recurring Job to run Every Hour
//jobs.RecurringCommand<MyCommand>(Schedule.Hourly);
});
}

public class JobsHostedService(ILogger<JobsHostedService> log, IBackgroundJobs jobs) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await jobs.StartAsync(stoppingToken);

using var timer = new PeriodicTimer(TimeSpan.FromSeconds(3));
while (!stoppingToken.IsCancellationRequested && await timer.WaitForNextTickAsync(stoppingToken))
{
await jobs.TickAsync();
}
}
}

/// <summary>
/// Sends emails by executing SendEmailCommand in a background job where it's serially processed by 'smtp' worker
/// </summary>
public class EmailSender(IBackgroundJobs jobs) : IEmailSender<ApplicationUser>
{
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
jobs.EnqueueCommand<SendEmailCommand>(new SendEmail {
To = email,
Subject = subject,
BodyHtml = 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}");
}
64 changes: 0 additions & 64 deletions MyApp/Configure.Mq.cs

This file was deleted.

1 change: 1 addition & 0 deletions MyApp/MyApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<PackageReference Include="ServiceStack" Version="8.*" />
<PackageReference Include="ServiceStack.Server" Version="8.*" />
<PackageReference Include="ServiceStack.Ormlite.Sqlite.Data" Version="8.*" />
<PackageReference Include="ServiceStack.Jobs" Version="8.*" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion MyApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
.PersistKeysToFileSystem(new DirectoryInfo("App_Data"));

// Add application services.
services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
// services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
// Uncomment to send emails with SMTP, configure SMTP with "SmtpConfig" in appsettings.json
services.AddSingleton<IEmailSender<ApplicationUser>, EmailSender>();
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AdditionalUserClaimsPrincipalFactory>();

// Register all services
Expand Down
2 changes: 1 addition & 1 deletion MyApp/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "metadata",
"applicationUrl": "http://localhost:5122",
"applicationUrl": "https://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
Expand Down
7 changes: 7 additions & 0 deletions NuGet.Config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="ServiceStack MyGet feed" value="https://www.myget.org/F/servicestack" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>

0 comments on commit 0a5943d

Please sign in to comment.