@@ -2,7 +2,6 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ROLAC.API.DTOs.Notifications;
|
||||
using ROLAC.API.Services.Notifications;
|
||||
|
||||
@@ -22,14 +21,14 @@ public sealed class LineWebhookController : ControllerBase
|
||||
|
||||
private readonly ILineNotificationService _line;
|
||||
private readonly IMessageChannel _channel;
|
||||
private readonly LineOptions _options;
|
||||
private readonly INotificationSettingsService _settings;
|
||||
|
||||
public LineWebhookController(
|
||||
ILineNotificationService line, IMessageChannel channel, IOptions<LineOptions> options)
|
||||
ILineNotificationService line, IMessageChannel channel, INotificationSettingsService settings)
|
||||
{
|
||||
_line = line;
|
||||
_channel = channel;
|
||||
_options = options.Value;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
[HttpPost("webhook")]
|
||||
@@ -40,7 +39,7 @@ public sealed class LineWebhookController : ControllerBase
|
||||
var rawBody = await reader.ReadToEndAsync(ct);
|
||||
var signature = Request.Headers["X-Line-Signature"].FirstOrDefault();
|
||||
|
||||
if (!LineSignature.IsValid(_options.ChannelSecret, Encoding.UTF8.GetBytes(rawBody), signature))
|
||||
if (!LineSignature.IsValid(_settings.GetLine().ChannelSecret, Encoding.UTF8.GetBytes(rawBody), signature))
|
||||
return BadRequest();
|
||||
|
||||
var payload = JsonSerializer.Deserialize<LineWebhookPayload>(rawBody, JsonOpts);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ROLAC.API.Authorization;
|
||||
using ROLAC.API.DTOs.Settings;
|
||||
using ROLAC.API.Services;
|
||||
using ROLAC.API.Services.Logging;
|
||||
using ROLAC.API.Services.Notifications;
|
||||
|
||||
namespace ROLAC.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Site-wide and notification (SMTP/Line) settings, surfaced by the Church Profile → Site /
|
||||
/// Notification tabs. Gated by the <c>Settings</c> permission module (super_admin bypasses).
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/settings")]
|
||||
[Authorize]
|
||||
public class SettingsController : ControllerBase
|
||||
{
|
||||
private readonly ISettingsService _settings;
|
||||
private readonly IEmailService _email;
|
||||
private readonly ILineNotificationService _line;
|
||||
private readonly CurrentUserAccessor _currentUser;
|
||||
|
||||
public SettingsController(
|
||||
ISettingsService settings,
|
||||
IEmailService email,
|
||||
ILineNotificationService line,
|
||||
CurrentUserAccessor currentUser)
|
||||
{
|
||||
_settings = settings;
|
||||
_email = email;
|
||||
_line = line;
|
||||
_currentUser = currentUser;
|
||||
}
|
||||
|
||||
// ── Site settings ────────────────────────────────────────────────────────
|
||||
|
||||
[HttpGet("site")]
|
||||
[HasPermission(Modules.Settings, PermissionActions.Read)]
|
||||
public async Task<IActionResult> GetSite() => Ok(await _settings.GetSiteAsync());
|
||||
|
||||
[HttpPut("site")]
|
||||
[HasPermission(Modules.Settings, PermissionActions.Write)]
|
||||
public async Task<IActionResult> UpdateSite([FromBody] UpdateSiteSettingRequest request)
|
||||
{
|
||||
await _settings.UpdateSiteAsync(request);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// ── Notification settings ──────────────────────────────────────────────────
|
||||
|
||||
[HttpGet("notification")]
|
||||
[HasPermission(Modules.Settings, PermissionActions.Read)]
|
||||
public async Task<IActionResult> GetNotification()
|
||||
{
|
||||
var dto = await _settings.GetNotificationAsync();
|
||||
dto.WebhookUrl = $"{Request.Scheme}://{Request.Host}/api/line/webhook";
|
||||
return Ok(dto);
|
||||
}
|
||||
|
||||
[HttpPut("notification")]
|
||||
[HasPermission(Modules.Settings, PermissionActions.Write)]
|
||||
public async Task<IActionResult> UpdateNotification([FromBody] UpdateNotificationSettingRequest request)
|
||||
{
|
||||
await _settings.UpdateNotificationAsync(request);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("notification/test-email")]
|
||||
[HasPermission(Modules.Settings, PermissionActions.Write)]
|
||||
public async Task<IActionResult> TestEmail([FromBody] TestEmailRequest request, CancellationToken ct)
|
||||
{
|
||||
var to = string.IsNullOrWhiteSpace(request.ToAddress) ? _currentUser.Email : request.ToAddress;
|
||||
if (string.IsNullOrWhiteSpace(to))
|
||||
return BadRequest(new { message = "No recipient — provide an address or set an email on your account." });
|
||||
|
||||
var result = await _email.SendAsync(new EmailMessage(
|
||||
MemberIds: Array.Empty<int>(),
|
||||
Addresses: new[] { to },
|
||||
Subject: "ROLAC test email / 測試郵件",
|
||||
HtmlBody: "<p>This is a test email from ROLAC notification settings.</p>"
|
||||
+ "<p>這是來自 ROLAC 通知設定的測試郵件。</p>",
|
||||
SentByUserId: _currentUser.UserIdOrSystem), ct);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPost("notification/test-line")]
|
||||
[HasPermission(Modules.Settings, PermissionActions.Write)]
|
||||
public async Task<IActionResult> TestLine([FromBody] TestLineRequest request, CancellationToken ct)
|
||||
{
|
||||
if (request.MemberId is null && request.GroupId is null)
|
||||
return BadRequest(new { message = "Choose a bound member or group to receive the test." });
|
||||
|
||||
var result = await _line.SendLineAsync(
|
||||
body: "ROLAC 測試訊息 / This is a test Line message from ROLAC.",
|
||||
memberIds: request.MemberId is { } m ? new[] { m } : Array.Empty<int>(),
|
||||
groupIds: request.GroupId is { } g ? new[] { g } : Array.Empty<int>(),
|
||||
sentByUserId: _currentUser.UserIdOrSystem,
|
||||
ct);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user