🐛 Fix magic spell and email service

This commit is contained in:
LittleSheep 2025-04-29 21:52:35 +08:00
parent 0ebeab672b
commit 35792efa9f
10 changed files with 56 additions and 38 deletions

View File

@ -20,7 +20,7 @@ public class EmailService
public EmailService(IConfiguration configuration) public EmailService(IConfiguration configuration)
{ {
var cfg = configuration.GetValue<EmailServiceConfiguration>("Email"); var cfg = configuration.GetSection("Email").Get<EmailServiceConfiguration>();
_configuration = cfg ?? throw new ArgumentException("Email service was not configured."); _configuration = cfg ?? throw new ArgumentException("Email service was not configured.");
} }

View File

@ -44,7 +44,7 @@ public class MagicSpellService(AppDatabase db, EmailService email, ILogger<Magic
if (contact is null) throw new ArgumentException("Account has no contact method that can use"); if (contact is null) throw new ArgumentException("Account has no contact method that can use");
// TODO replace the baseurl // TODO replace the baseurl
var link = $"https://api.sn.solsynth.dev/spells/{spell}"; var link = $"https://api.sn.solsynth.dev/spells/{Uri.EscapeDataString(spell.Spell)}";
try try
{ {
@ -102,6 +102,7 @@ public class MagicSpellService(AppDatabase db, EmailService email, ILogger<Magic
}); });
} }
db.Remove(spell);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
break; break;
default: default:

View File

@ -25,6 +25,12 @@ public class AuthService(IConfiguration config, IHttpClientFactory httpClientFac
var client = httpClientFactory.CreateClient(); var client = httpClientFactory.CreateClient();
var jsonOpts = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower
};
switch (provider) switch (provider)
{ {
case "cloudflare": case "cloudflare":
@ -35,9 +41,9 @@ public class AuthService(IConfiguration config, IHttpClientFactory httpClientFac
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync(); var json = await response.Content.ReadAsStringAsync();
var cfResult = JsonSerializer.Deserialize<CloudflareVerificationResponse>(json); var result = JsonSerializer.Deserialize<CaptchaVerificationResponse>(json, options: jsonOpts);
return cfResult?.Success == true; return result?.Success == true;
case "google": case "google":
content = new StringContent($"secret={apiSecret}&response={token}", System.Text.Encoding.UTF8, content = new StringContent($"secret={apiSecret}&response={token}", System.Text.Encoding.UTF8,
"application/x-www-form-urlencoded"); "application/x-www-form-urlencoded");
@ -45,9 +51,19 @@ public class AuthService(IConfiguration config, IHttpClientFactory httpClientFac
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
json = await response.Content.ReadAsStringAsync(); json = await response.Content.ReadAsStringAsync();
var capResult = JsonSerializer.Deserialize<GoogleVerificationResponse>(json); result = JsonSerializer.Deserialize<CaptchaVerificationResponse>(json, options: jsonOpts);
return capResult?.Success == true; return result?.Success == true;
case "hcaptcha":
content = new StringContent($"secret={apiSecret}&response={token}", System.Text.Encoding.UTF8,
"application/x-www-form-urlencoded");
response = await client.PostAsync("https://hcaptcha.com/siteverify", content);
response.EnsureSuccessStatusCode();
json = await response.Content.ReadAsStringAsync();
result = JsonSerializer.Deserialize<CaptchaVerificationResponse>(json, options: jsonOpts);
return result?.Success == true;
default: default:
throw new ArgumentException("The server misconfigured for the captcha."); throw new ArgumentException("The server misconfigured for the captcha.");
} }

View File

@ -1,17 +1,6 @@
namespace DysonNetwork.Sphere.Auth; namespace DysonNetwork.Sphere.Auth;
public class CloudflareVerificationResponse public class CaptchaVerificationResponse
{ {
public bool Success { get; set; } public bool Success { get; set; }
public string[]? ErrorCodes { get; set; }
}
public class GoogleVerificationResponse
{
public bool Success { get; set; }
public float Score { get; set; }
public string Action { get; set; }
public DateTime ChallengeTs { get; set; }
public string Hostname { get; set; }
public string[]? ErrorCodes { get; set; }
} }

View File

@ -1,5 +1,5 @@
@page "/auth/captcha" @page "/auth/captcha"
@model DysonNetwork.Sphere.Pages.CheckpointPage @model DysonNetwork.Sphere.Pages.Checkpoint.CheckpointPage
@{ @{
Layout = null; Layout = null;
@ -73,6 +73,9 @@
defer defer
></script> ></script>
break; break;
case "hcaptcha":
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
break;
} }
</head> </head>
<body> <body>
@ -95,6 +98,13 @@
data-callback="onSuccess" data-callback="onSuccess"
></div> ></div>
break; break;
case "hcaptcha":
<div
class="h-captcha"
data-sitekey="@apiKey"
data-callback="onSuccess"
></div>
break;
default: default:
<p style="color: yellow;">Captcha provider not configured correctly.</p> <p style="color: yellow;">Captcha provider not configured correctly.</p>
break; break;

View File

@ -1,13 +1,14 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace DysonNetwork.Sphere.Pages; namespace DysonNetwork.Sphere.Pages.Checkpoint;
public class CheckpointPage(IConfiguration configuration) : PageModel public class CheckpointPage(IConfiguration configuration) : PageModel
{ {
[BindProperty] public IConfiguration Configuration { get; set; } = configuration; [BindProperty] public IConfiguration Configuration { get; set; } = configuration;
public void OnGet() public ActionResult OnGet()
{ {
return Page();
} }
} }

View File

@ -1,6 +1,6 @@
@page "/spells/{spellWord}" @page "/spells/{spellWord}"
@using DysonNetwork.Sphere.Account @using DysonNetwork.Sphere.Account
@model DysonNetwork.Sphere.Pages.MagicSpellPage @model DysonNetwork.Sphere.Pages.Spell.MagicSpellPage
@{ @{
Layout = null; Layout = null;
@ -8,8 +8,6 @@
var spell = ViewData["Spell"] as MagicSpell; var spell = ViewData["Spell"] as MagicSpell;
} }
<!DOCTYPE html>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -57,10 +55,6 @@
margin-bottom: 5px; margin-bottom: 5px;
opacity: 0.8; opacity: 0.8;
} }
.g-recaptcha {
display: inline-block; /* Adjust as needed */
}
</style> </style>
</head> </head>
<body> <body>

View File

@ -4,21 +4,27 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
namespace DysonNetwork.Sphere.Pages; namespace DysonNetwork.Sphere.Pages.Spell;
public class MagicSpellPage(AppDatabase db, MagicSpellService spells) : PageModel public class MagicSpellPage(AppDatabase db, MagicSpellService spells) : PageModel
{ {
public async Task<ActionResult> OnGet(string spellWord) public async Task<IActionResult> OnGetAsync(string spellWord)
{ {
spellWord = Uri.UnescapeDataString(spellWord);
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
var spell = await db.MagicSpells var spell = await db.MagicSpells
.Where(e => e.Spell == spellWord) .Where(e => e.Spell == spellWord)
.Where(e => e.ExpiresAt == null || now >= e.ExpiresAt) .Where(e => e.ExpiresAt == null || now < e.ExpiresAt)
.Where(e => e.AffectedAt == null || now >= e.AffectedAt) .Where(e => e.AffectedAt == null || now >= e.AffectedAt)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
ViewData["Spell"] = spell; ViewData["Spell"] = spell;
if (spell is not null)
{
await spells.ApplyMagicSpell(spell);
}
return Page(); return Page();
} }
} }

View File

@ -45,8 +45,8 @@
] ]
}, },
"Captcha": { "Captcha": {
"Provider": "recaptcha", "Provider": "cloudflare",
"ApiKey": "6LfIzSArAAAAAN413MtycDcPlKa636knBSAhbzj-", "ApiKey": "0x4AAAAAABCDUdOujj4feOb_",
"ApiSecret": "" "ApiSecret": ""
}, },
"Notifications": { "Notifications": {
@ -62,12 +62,12 @@
} }
}, },
"Email": { "Email": {
"Server": "", "Server": "smtpdm.aliyun.com",
"Port": 465, "Port": 465,
"Username": "", "Username": "no-reply@mail.solsynth.dev",
"Password": "", "Password": "",
"FromAddress": "", "FromAddress": "no-reply@mail.solsynth.dev",
"FromName": "", "FromName": "Alphabot",
"SubjectPrefix": "Solar Network" "SubjectPrefix": "Solar Network"
} }
} }

View File

@ -28,6 +28,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIntentType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003Fbf_003Ffcb84131_003FIntentType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIntentType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003Fbf_003Ffcb84131_003FIntentType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIServiceCollectionQuartzConfigurator_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1edbd6e24d7b430fabce72177269baa19200_003F67_003Faee36f5b_003FIServiceCollectionQuartzConfigurator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIServiceCollectionQuartzConfigurator_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1edbd6e24d7b430fabce72177269baa19200_003F67_003Faee36f5b_003FIServiceCollectionQuartzConfigurator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AITusStore_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003Fb1_003F7e861de5_003FITusStore_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AITusStore_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003Fb1_003F7e861de5_003FITusStore_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJsonSerializerOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5703920a18f94462b4354fab05326e6519a200_003F35_003F8536fc49_003FJsonSerializerOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKestrelServerLimits_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1e2e5dfcafad4407b569dd5df56a2fbf274e00_003Fa4_003F39445f62_003FKestrelServerLimits_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKestrelServerLimits_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1e2e5dfcafad4407b569dd5df56a2fbf274e00_003Fa4_003F39445f62_003FKestrelServerLimits_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMailboxAddress_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8e03e47c46b7469f97abc40667cbcf9b133000_003Fa6_003F83324248_003FMailboxAddress_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMailboxAddress_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8e03e47c46b7469f97abc40667cbcf9b133000_003Fa6_003F83324248_003FMailboxAddress_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMediaAnalysis_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffef366b36a224d469ff150d30f9a866d23c00_003Fd7_003F5c138865_003FMediaAnalysis_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMediaAnalysis_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffef366b36a224d469ff150d30f9a866d23c00_003Fd7_003F5c138865_003FMediaAnalysis_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>