Add German localization and styling for administration module
- Introduced new JavaScript file `fis.admin_txt_de.js` for German translations of administration-related terms and messages. - Created `fis.admin.css` for styling the administration interface, including layout, cards, and buttons. - Added `fis.admin.de.js` for the main functionality of the administration module, implementing features such as system status checks and email testing. - Minified version of the German JavaScript file created as `fis.admin.de.min.js`. - Minified CSS file created as `fis.admin.min.css` for optimized loading.
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
using Fuchs.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using OCORE.SQL;
|
||||
using static OCORE.SQL.sql;
|
||||
using static OCORE.web.mvc_helper_async;
|
||||
|
||||
namespace Fuchs.Controllers;
|
||||
|
||||
// Partial class: Admin / system-status module.
|
||||
//
|
||||
// Access is restricted to users whose "fds_sys" module authorization is greater than 4.
|
||||
// The menu button is only shown, and the module script only loaded, for such users (frontend),
|
||||
// but every data endpoint here ALSO enforces the level server-side (defense in depth) — the
|
||||
// passive/probe data is diagnostic and must never be reachable by a lower-privileged session.
|
||||
public partial class IntranetController
|
||||
{
|
||||
// fds_sys authorization must exceed this to use the Admin module.
|
||||
private const int AdminMinAuthExclusive = 4;
|
||||
|
||||
private async Task<IActionResult> Do_Process_Admin(string fn, string id, string code)
|
||||
{
|
||||
_logger.LogDebug("Do_Process_Admin action={Action} code={Code} user={User}", id, code, UserAccountID);
|
||||
|
||||
// The auth probe is the one endpoint that answers for BOTH authorized and unauthorized
|
||||
// users (the frontend uses manage>0 to decide whether to render the module at all).
|
||||
int authLevel = await GetSystemAdminAuthAsync(fn, id, code);
|
||||
bool authorized = authLevel > AdminMinAuthExclusive;
|
||||
|
||||
if (id.Equals("auth", StringComparison.OrdinalIgnoreCase))
|
||||
return await JSONAsync(new { manage = authorized ? 1 : 0, level = authLevel });
|
||||
|
||||
if (!authorized)
|
||||
{
|
||||
_logger.LogWarning("Admin access denied for user={User} (fds_sys={Level}) action={Action}",
|
||||
UserAccountID, authLevel, id);
|
||||
return Unauthorized401();
|
||||
}
|
||||
|
||||
var status = _systemStatus;
|
||||
switch (id.ToLowerInvariant())
|
||||
{
|
||||
case "status":
|
||||
{
|
||||
var info = status.GetInfo();
|
||||
var probes = await status.ProbeAllAsync(HttpContext.RequestAborted);
|
||||
return AdminJson(new { info, probes });
|
||||
}
|
||||
|
||||
case "info":
|
||||
return AdminJson(new { info = status.GetInfo() });
|
||||
|
||||
case "probe":
|
||||
{
|
||||
// code carries the component id, e.g. /do/admin/probe/database
|
||||
string component = string.IsNullOrWhiteSpace(code) ? Form("component") : code;
|
||||
if (string.IsNullOrWhiteSpace(component))
|
||||
return BadRequest400();
|
||||
var probe = await status.ProbeAsync(component, HttpContext.RequestAborted);
|
||||
return AdminJson(new { probe });
|
||||
}
|
||||
|
||||
case "testmail":
|
||||
{
|
||||
if (!HasForm("to", "subject"))
|
||||
return BadRequest400();
|
||||
var result = await status.SendTestEmailAsync(
|
||||
Form("to"), Form("subject"), Form("body"), HttpContext.RequestAborted);
|
||||
_logger.LogInformation("Admin test email requested by user={User} to={To} sent={Sent}",
|
||||
UserAccountID, result.RequestedRecipient, result.Sent);
|
||||
return AdminJson(new { result });
|
||||
}
|
||||
|
||||
default:
|
||||
_logger.LogWarning("Admin: no handler for action={Action}, user={User}", id, UserAccountID);
|
||||
return BadRequest400();
|
||||
}
|
||||
}
|
||||
|
||||
// The status DTOs (SystemInfoSnapshot / SystemProbeResult) are PascalCase; serialize them
|
||||
// camelCase so the Admin frontend contract matches the lowercase convention used elsewhere.
|
||||
private static readonly JsonSerializerSettings CamelCaseJson = new()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
};
|
||||
|
||||
private ContentResult AdminJson(object payload) =>
|
||||
Content(JsonConvert.SerializeObject(payload, CamelCaseJson), "application/json");
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the calling user's <c>fds_sys</c> module authorization level via the
|
||||
/// <c>fis_getModuleAuth</c> SQL function (same mechanism as <see cref="HandleAuth"/>).
|
||||
/// Returns -3 when it cannot be determined (fail-closed).
|
||||
/// </summary>
|
||||
private async Task<int> GetSystemAdminAuthAsync(string fn, string id, string code)
|
||||
{
|
||||
var val = await getSQLValue_async<int>(
|
||||
"SELECT [dbo].[fis_getModuleAuth](@module, @authuser);",
|
||||
_intranet.Intranet__SQLConnectionString, -3,
|
||||
StdParamlist(SQL_VarChar("@module", "fds_sys")),
|
||||
Security: DbSec, options: SqlOpt(fn, id, code));
|
||||
return val.Result;
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
private readonly IInvoiceDraftService _invoiceDrafts;
|
||||
private readonly IReminderDraftService _reminderDrafts;
|
||||
private readonly IDraftNotifier _draftNotifier;
|
||||
private readonly ISystemStatusService _systemStatus;
|
||||
private readonly List<string> _allowedNonAuth = new() { "spwc", "spw" };
|
||||
private readonly List<string> _allowedGet = new()
|
||||
{
|
||||
@@ -68,7 +69,8 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
IEventService events,
|
||||
IInvoiceDraftService invoiceDrafts,
|
||||
IReminderDraftService reminderDrafts,
|
||||
IDraftNotifier draftNotifier)
|
||||
IDraftNotifier draftNotifier,
|
||||
ISystemStatusService systemStatus)
|
||||
{
|
||||
_intranet = intranet;
|
||||
_mfr = mfr;
|
||||
@@ -85,6 +87,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
_invoiceDrafts = invoiceDrafts;
|
||||
_reminderDrafts = reminderDrafts;
|
||||
_draftNotifier = draftNotifier;
|
||||
_systemStatus = systemStatus;
|
||||
}
|
||||
|
||||
/// <summary>Merged query-string + form parameters (form wins) for report processing.</summary>
|
||||
@@ -163,6 +166,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
"rem" => await Do_Process_Reminder(fn, id, code),
|
||||
"rep" => await Do_Process_Reports(fn, id, code),
|
||||
"bam" => await Do_Process_Bankings(fn, id, code),
|
||||
"admin" => await Do_Process_Admin(fn, id, code),
|
||||
"auth" => await HandleAuth(fn, id, code),
|
||||
"spwc" => await HandleSendPasswordCode(fn, id, code),
|
||||
"spw" => await HandleSendPassword(fn, id, code),
|
||||
|
||||
Reference in New Issue
Block a user