Skip to content

Commit a478299

Browse files
authored
feat: Take Options pattern into use (#168)
* feat: Take Options pattern into use * Oops
1 parent b59bb16 commit a478299

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+561
-448
lines changed

API/ApiConfig.cs

Lines changed: 0 additions & 62 deletions
This file was deleted.

API/Controller/Account/Login.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
using Microsoft.AspNetCore.Mvc;
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Extensions.Options;
24
using OpenShock.API.Models.Requests;
3-
using System.Net;
4-
using System.Net.Mime;
5-
using Asp.Versioning;
65
using OpenShock.API.Services.Account;
7-
using OpenShock.Common;
8-
using OpenShock.Common.Constants;
96
using OpenShock.Common.Errors;
7+
using OpenShock.Common.Models;
8+
using OpenShock.Common.Options;
109
using OpenShock.Common.Problems;
1110
using OpenShock.Common.Utils;
12-
using OpenShock.Common.Models;
11+
using System.Net.Mime;
1312

1413
namespace OpenShock.API.Controller.Account;
1514

@@ -27,12 +26,12 @@ public sealed partial class AccountController
2726
[MapToApiVersion("1")]
2827
public async Task<IActionResult> Login(
2928
[FromBody] Login body,
30-
[FromServices] ApiConfig apiConfig,
29+
[FromServices] IOptions<FrontendOptions> options,
3130
CancellationToken cancellationToken)
3231
{
33-
var cookieDomainToUse = apiConfig.Frontend.CookieDomain.Split(',').FirstOrDefault(domain => Request.Headers.Host.ToString().EndsWith(domain, StringComparison.OrdinalIgnoreCase));
32+
var cookieDomainToUse = options.Value.CookieDomain.Split(',').FirstOrDefault(domain => Request.Headers.Host.ToString().EndsWith(domain, StringComparison.OrdinalIgnoreCase));
3433
if (cookieDomainToUse == null) return Problem(LoginError.InvalidDomain);
35-
34+
3635
var loginAction = await _accountService.Login(body.Email, body.Password, new LoginContext
3736
{
3837
Ip = HttpContext.GetRemoteIP().ToString(),

API/Controller/Account/LoginV2.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using OpenShock.Common.Services.Turnstile;
1212
using OpenShock.Common.Utils;
1313
using OpenShock.Common.Models;
14+
using Microsoft.Extensions.Options;
15+
using OpenShock.Common.Options;
1416

1517
namespace OpenShock.API.Controller.Account;
1618

@@ -29,10 +31,10 @@ public sealed partial class AccountController
2931
public async Task<IActionResult> LoginV2(
3032
[FromBody] LoginV2 body,
3133
[FromServices] ICloudflareTurnstileService turnstileService,
32-
[FromServices] ApiConfig apiConfig,
34+
[FromServices] IOptions<FrontendOptions> options,
3335
CancellationToken cancellationToken)
3436
{
35-
var cookieDomainToUse = apiConfig.Frontend.CookieDomain.Split(',').FirstOrDefault(domain => Request.Headers.Host.ToString().EndsWith(domain, StringComparison.OrdinalIgnoreCase));
37+
var cookieDomainToUse = options.Value.CookieDomain.Split(',').FirstOrDefault(domain => Request.Headers.Host.ToString().EndsWith(domain, StringComparison.OrdinalIgnoreCase));
3638
if (cookieDomainToUse == null) return Problem(LoginError.InvalidDomain);
3739

3840
var remoteIP = HttpContext.GetRemoteIP();

API/Controller/Account/Logout.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Asp.Versioning;
22
using Microsoft.AspNetCore.Mvc;
3-
using OpenShock.Common.Problems;
3+
using Microsoft.Extensions.Options;
4+
using OpenShock.Common.Options;
45
using OpenShock.Common.Services.Session;
56
using OpenShock.Common.Utils;
67

@@ -13,23 +14,25 @@ public sealed partial class AccountController
1314
[MapToApiVersion("1")]
1415
public async Task<IActionResult> Logout(
1516
[FromServices] ISessionService sessionService,
16-
[FromServices] ApiConfig apiConfig)
17+
[FromServices] IOptions<FrontendOptions> options)
1718
{
19+
var config = options.Value;
20+
1821
// Remove session if valid
1922
if (HttpContext.TryGetUserSession(out var sessionCookie))
2023
{
2124
await sessionService.DeleteSessionById(sessionCookie);
2225
}
23-
26+
2427
// Make sure cookie is removed, no matter if authenticated or not
25-
var cookieDomainToUse = apiConfig.Frontend.CookieDomain.Split(',').FirstOrDefault(domain => Request.Headers.Host.ToString().EndsWith(domain, StringComparison.OrdinalIgnoreCase));
28+
var cookieDomainToUse = config.CookieDomain.Split(',').FirstOrDefault(domain => Request.Headers.Host.ToString().EndsWith(domain, StringComparison.OrdinalIgnoreCase));
2629
if (cookieDomainToUse != null)
2730
{
2831
HttpContext.RemoveSessionKeyCookie("." + cookieDomainToUse);
2932
}
3033
else // Fallback to all domains
3134
{
32-
foreach (var domain in apiConfig.Frontend.CookieDomain.Split(','))
35+
foreach (var domain in config.CookieDomain.Split(','))
3336
{
3437
HttpContext.RemoveSessionKeyCookie("." + domain);
3538
}

API/Controller/Admin/GetUsers.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
using System.ComponentModel.DataAnnotations;
2-
using System.Net.Mime;
3-
using Microsoft.AspNetCore.Mvc;
1+
using Microsoft.AspNetCore.Mvc;
42
using Microsoft.EntityFrameworkCore;
53
using OpenShock.Common.Errors;
64
using OpenShock.Common.Extensions;
75
using OpenShock.Common.Models;
8-
using OpenShock.Common.Utils;
9-
using Z.EntityFramework.Plus;
106
using OpenShock.Common.OpenShockDb;
117
using OpenShock.Common.Query;
8+
using System.ComponentModel.DataAnnotations;
9+
using System.Net.Mime;
10+
using Z.EntityFramework.Plus;
1211

1312
namespace OpenShock.API.Controller.Admin;
1413

@@ -24,8 +23,8 @@ public sealed partial class AdminController
2423
public async Task<IActionResult> GetUsers(
2524
[FromQuery(Name = "$filter")] string filterQuery = "",
2625
[FromQuery(Name = "$orderby")] string orderbyQuery = "",
27-
[FromQuery(Name = "$offset")] [Range(0, int.MaxValue)] int offset = 0,
28-
[FromQuery(Name = "$limit")] [Range(1, 1000)] int limit = 100
26+
[FromQuery(Name = "$offset")][Range(0, int.MaxValue)] int offset = 0,
27+
[FromQuery(Name = "$limit")][Range(1, 1000)] int limit = 100
2928
)
3029
{
3130
var deferredCount = _db.Users.DeferredLongCount().FutureValue();
@@ -65,7 +64,7 @@ public async Task<IActionResult> GetUsers(
6564
{
6665
query = query.Skip(offset);
6766
}
68-
67+
6968
var deferredUsers = query.Take(limit).Future();
7069

7170
return Ok(new Paginated<AdminUsersView>

API/Controller/Version/_ApiController.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
using System.Net.Mime;
2-
using System.Reflection;
3-
using Microsoft.AspNetCore.Authorization;
4-
using Microsoft.AspNetCore.Mvc;
5-
using OpenShock.API.Utils;
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.Extensions.Options;
63
using OpenShock.Common;
74
using OpenShock.Common.Models;
8-
using OpenShock.Common.Problems;
5+
using OpenShock.Common.Options;
96
using OpenShock.Common.Utils;
7+
using System.Net.Mime;
8+
using System.Reflection;
109

1110
namespace OpenShock.API.Controller.Version;
1211

@@ -27,16 +26,23 @@ public sealed partial class VersionController : OpenShockControllerBase
2726
/// <response code="200">The version was successfully retrieved.</response>
2827
[HttpGet]
2928
[ProducesResponseType<BaseResponse<RootResponse>>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)]
30-
public IActionResult GetBackendVersion([FromServices] ApiConfig apiConfig)
29+
public IActionResult GetBackendVersion(
30+
[FromServices] IOptions<FrontendOptions> frontendOptions,
31+
[FromServices] IOptions<CloudflareTurnstileOptions> turnstileOptions
32+
)
3133
{
34+
var frontendConfig = frontendOptions.Value;
35+
var turnstileConfig = turnstileOptions.Value;
36+
3237
return RespondSuccessLegacy(
33-
data: new RootResponse {
38+
data: new RootResponse
39+
{
3440
Version = OpenShockBackendVersion,
3541
Commit = GitHashAttribute.FullHash,
3642
CurrentTime = DateTimeOffset.UtcNow,
37-
FrontendUrl = apiConfig.Frontend.BaseUrl,
38-
ShortLinkUrl = apiConfig.Frontend.ShortUrl,
39-
TurnstileSiteKey = apiConfig.Turnstile.SiteKey
43+
FrontendUrl = frontendConfig.BaseUrl,
44+
ShortLinkUrl = frontendConfig.ShortUrl,
45+
TurnstileSiteKey = turnstileConfig.SiteKey
4046
},
4147
message: "OpenShock"
4248
);

API/Options/MailJetOptions.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Microsoft.Extensions.Options;
2+
using System.ComponentModel.DataAnnotations;
3+
4+
namespace OpenShock.API.Options;
5+
6+
public sealed class MailJetOptions
7+
{
8+
public const string SectionName = MailOptions.SectionName + ":Mailjet";
9+
10+
[Required(AllowEmptyStrings = false)]
11+
public required string Key { get; init; }
12+
13+
[Required(AllowEmptyStrings = false)]
14+
public required string Secret { get; init; }
15+
16+
[Required]
17+
[ValidateObjectMembers]
18+
public required MailjetTemplateOptions Template { get; init; }
19+
20+
public sealed class MailjetTemplateOptions
21+
{
22+
[Required]
23+
public required ulong PasswordReset { get; init; }
24+
25+
[Required]
26+
public required ulong PasswordResetComplete { get; init; }
27+
28+
[Required]
29+
public required ulong VerifyEmail { get; init; }
30+
31+
[Required]
32+
public required ulong VerifyEmailComplete { get; init; }
33+
}
34+
}
35+
36+
[OptionsValidator]
37+
public partial class MailJetOptionsValidator : IValidateOptions<MailJetOptions>
38+
{
39+
}

API/Options/MailOptions.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Microsoft.Extensions.Options;
2+
using OpenShock.API.Services.Email.Mailjet.Mail;
3+
using System.ComponentModel.DataAnnotations;
4+
5+
namespace OpenShock.API.Options;
6+
7+
public sealed class MailOptions
8+
{
9+
public const string SectionName = "OpenShock:Mail";
10+
public const string SenderSectionName = SectionName + ":Sender";
11+
public const string SenderOptionName = "EmailSender";
12+
13+
[Required]
14+
public required MailType Type { get; init; }
15+
16+
[Required]
17+
[ValidateObjectMembers]
18+
public required Contact Sender { get; init; }
19+
20+
[ValidateObjectMembers]
21+
public MailJetOptions? Mailjet { get; init; }
22+
23+
[ValidateObjectMembers]
24+
public SmtpOptions? Smtp { get; init; }
25+
26+
public enum MailType
27+
{
28+
Mailjet = 0,
29+
Smtp = 1
30+
}
31+
}

API/Options/SmtpOptions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Microsoft.Extensions.Options;
2+
using System.ComponentModel.DataAnnotations;
3+
4+
namespace OpenShock.API.Options;
5+
6+
public sealed class SmtpOptions
7+
{
8+
public const string SectionName = MailOptions.SectionName + ":Smtp";
9+
10+
[Required(AllowEmptyStrings = false)]
11+
public required string Host { get; init; }
12+
13+
public ushort Port { get; init; } = 587;
14+
15+
public string Username { get; init; } = string.Empty;
16+
17+
public string Password { get; init; } = string.Empty;
18+
19+
public bool EnableSsl { get; init; } = true;
20+
21+
public bool VerifyCertificate { get; init; } = true;
22+
}
23+
24+
[OptionsValidator]
25+
public partial class SmtpOptionsValidator : IValidateOptions<SmtpOptions>
26+
{
27+
}

0 commit comments

Comments
 (0)