🐛 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)
{
var cfg = configuration.GetValue<EmailServiceConfiguration>("Email");
var cfg = configuration.GetSection("Email").Get<EmailServiceConfiguration>();
_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");
// 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
{
@ -102,6 +102,7 @@ public class MagicSpellService(AppDatabase db, EmailService email, ILogger<Magic
});
}
db.Remove(spell);
await db.SaveChangesAsync();
break;
default:

View File

@ -24,6 +24,12 @@ public class AuthService(IConfiguration config, IHttpClientFactory httpClientFac
var apiSecret = config.GetSection("Captcha")["ApiSecret"];
var client = httpClientFactory.CreateClient();
var jsonOpts = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower
};
switch (provider)
{
@ -33,11 +39,11 @@ public class AuthService(IConfiguration config, IHttpClientFactory httpClientFac
var response = await client.PostAsync("https://challenges.cloudflare.com/turnstile/v0/siteverify",
content);
response.EnsureSuccessStatusCode();
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":
content = new StringContent($"secret={apiSecret}&response={token}", System.Text.Encoding.UTF8,
"application/x-www-form-urlencoded");
@ -45,9 +51,19 @@ public class AuthService(IConfiguration config, IHttpClientFactory httpClientFac
response.EnsureSuccessStatusCode();
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:
throw new ArgumentException("The server misconfigured for the captcha.");
}

View File

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

View File

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

View File

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

View File

@ -4,21 +4,27 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Sphere.Pages;
namespace DysonNetwork.Sphere.Pages.Spell;
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 spell = await db.MagicSpells
.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)
.FirstOrDefaultAsync();
ViewData["Spell"] = spell;
if (spell is not null)
{
await spells.ApplyMagicSpell(spell);
}
return Page();
}
}

View File

@ -45,8 +45,8 @@
]
},
"Captcha": {
"Provider": "recaptcha",
"ApiKey": "6LfIzSArAAAAAN413MtycDcPlKa636knBSAhbzj-",
"Provider": "cloudflare",
"ApiKey": "0x4AAAAAABCDUdOujj4feOb_",
"ApiSecret": ""
},
"Notifications": {
@ -62,12 +62,12 @@
}
},
"Email": {
"Server": "",
"Server": "smtpdm.aliyun.com",
"Port": 465,
"Username": "",
"Username": "no-reply@mail.solsynth.dev",
"Password": "",
"FromAddress": "",
"FromName": "",
"FromAddress": "no-reply@mail.solsynth.dev",
"FromName": "Alphabot",
"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_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_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_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>