Files
Fuchs_Intranet/Fuchs/wwwroot/web/fis.admin.de.js
T
StefanandClaude Sonnet 5 107b7f54f1
Playwright Tests / test (push) Canceled after 0s
Verify mailer credentials against ProcessWeb API before showing E-Mail-Dienst as healthy
The Admin status pill previously turned green whenever the mailer was
enabled in config, without proving the account/token actually work.
Add IComService.VerifyCredentialsAsync (GET /mailer/fc?fn=verify, also
passing serverid when configured so the API confirms this server
belongs to the account) and wire it into a new "email" system-status
probe, so the pill only goes green once the Mailer API itself confirms
the credentials.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 23:51:32 +02:00

328 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
let $adt = {
mdl: 'Systemüberwachung'
, sub_info: 'System'
, sub_status: 'Status & Konnektivität'
, sub_tests: 'Tests'
// startup checks widget (non-refreshable)
, startup: 'Startup-Prüfungen'
, startup_notrun: 'Die Startup-Prüfungen sind deaktiviert oder wurden noch nicht ausgeführt (Fuchs:StartupChecks:Enabled).'
, startup_on: 'Host'
, startup_at: 'Ausgeführt (UTC)'
, chk_ok: 'OK'
, chk_fail: 'Fehler'
, chk_skipped: 'übersprungen'
, chk_KeyVault: 'Azure Key Vault'
, chk_Database: 'SQL-Datenbank'
, chk_MFR: 'MFR (ERP)'
, chk_PdfLicense: 'Spire.PDF-Lizenz'
, chk_Mailer: 'E-Mail (Startup-Probe)'
// system info
, host: 'Host / Server'
, machine: 'Hostname'
, os: 'Betriebssystem'
, framework: 'Framework'
, env: 'Umgebung'
, testdep: 'Test-Deployment'
, pid: 'Prozess-ID'
, uptime: 'Laufzeit'
, since: 'Start (UTC)'
, hours: 'Std.'
// status cards
, db: 'SQL-Datenbank'
, mail: 'E-Mail-Dienst'
, blob: 'Azure Blob Storage'
, mfr: 'MFR (ERP)'
, kv: 'Azure Key Vault'
// labels
, server: 'Server'
, catalog: 'Datenbank'
, login: 'Login'
, mailer: 'Mailer aktiv'
, baseurl: 'Base-URL'
, account: 'Account'
, serverid: 'Server-ID'
, token: 'Token hinterlegt'
, override: 'OverrideRecipient'
, overrideActive: 'Umleitung aktiv'
, enabled: 'Aktiviert'
, configured: 'Konfiguriert'
, invContainer: 'Rechnungs-Container'
, remContainer: 'Mahnungs-Container'
, host_l: 'Host'
, creds: 'Zugangsdaten hinterlegt'
, sync: 'Sync aktiv'
, vault: 'Vault-URI'
, appname: 'App-Präfix'
, seccount: 'Verwaltete Secrets'
, clientreg: 'Client registriert'
, duration: 'Dauer'
, checked: 'Geprüft'
, detail: 'Detail'
// status values
, st_ok: 'OK'
, st_error: 'Fehler'
, st_disabled: 'Deaktiviert'
, st_unconfigured: 'Nicht konfiguriert'
, st_checking: 'Prüfe…'
// actions
, refresh: 'Aktualisieren'
, refreshAll: 'Alle prüfen'
, yes: 'Ja'
, no: 'Nein'
// test email
, testmail: 'Test-E-Mail'
, testmail_btn: 'Test-E-Mail senden'
, tm_to: 'Empfänger'
, tm_subject: 'Betreff'
, tm_body: 'Nachricht'
, tm_hint: 'Der Versand nutzt die normale E-Mail-Pipeline; bei gesetztem OverrideRecipient wird die Nachricht dorthin umgeleitet.'
, tm_sent: 'Test-E-Mail übergeben.'
, tm_failed: 'Test-E-Mail konnte nicht gesendet werden.'
, tm_default_subject: 'Fuchs Intranet – Test-E-Mail'
, tm_default_body: 'Dies ist eine Test-E-Mail aus dem Administrationsbereich des Fuchs Intranet.'
, loadfail: 'Der Systemstatus konnte nicht geladen werden.'
};
let $adm = {
init2: function () {
// No external libraries needed — go straight to render.
$ocms.getScript([], function () { $adm.init3(); });
},
init3: function () {
$fis.cf(true);
$fis.lf(true);
$('#activemodule').text($adt.mdl);
$adm.topbar();
let frm = $$.dc('admfrm', $('#contentframe')).ldng(1);
$adm.load(frm);
},
topbar: function () {
return $('#topbar').ocmsmenu([
{ lbl: $adt.refreshAll, id: 'adm_refresh_all', ico: 'glyphicon glyphicon-refresh', fnc: function () { $adm.init3(); } }
]);
},
load: function (frm) {
$ocms.postXT({
url: $ocms.url('admin/status'), success: function (r) {
frm.empty();
$adm.render(frm, r.info || {}, r.probes || []);
}, error: function () {
frm.empty();
$$.dc('adm_err', frm).text($adt.loadfail);
}, complete: function () { frm.ldng(0); }
});
},
render: function (frm, info, probes) {
let pmap = {};
$.each(probes, function (i, p) { pmap[p.component] = p; });
$$.dc('adm_sec', frm).text($adt.sub_info);
$adm.sysCard(frm, info);
$adm.startupCard(frm, info.startupChecks);
$$.dc('adm_sec', frm).text($adt.sub_status);
let grid = $$.dc('adm_grid', frm);
$adm.dbCard(grid, info.database || {}, pmap.database);
$adm.mailCard(grid, info.email || {}, pmap.email);
$adm.blobCard(grid, info.blob || {}, pmap.blob);
$adm.mfrCard(grid, info.mfr || {}, pmap.mfr);
$adm.kvCard(grid, info.keyVault || {}, pmap.keyvault);
},
/* ── generic building blocks ─────────────────────────────────────────── */
kvTable: function (parent, rows) {
let t = $$.tbl().addClass('adm_kv');
$.each(rows || [], function (i, r) {
if (!r) { return true; }
let tr = $$.tr(t);
$$.td(tr).addClass('k').text(r[0] == null ? '' : String(r[0]));
let v = $$.td(tr).addClass('v');
if (r[1] instanceof jQuery) { v.append(r[1]); } else { v.text(r[1] == null ? '' : String(r[1])); }
});
if (parent instanceof jQuery) { parent.append(t); }
return t;
},
yesNo: function (b) {
return $$.sc('adm_bool adm_bool_' + (b ? 'y' : 'n'), b ? $adt.yes : $adt.no);
},
setPill: function (pill, probe) {
probe = probe || {};
let st = probe.status || 'unconfigured';
let lbl = {
ok: $adt.st_ok, error: $adt.st_error, disabled: $adt.st_disabled,
unconfigured: $adt.st_unconfigured, checking: $adt.st_checking
}[st] || st;
pill.attr('class', 'adm_pill adm_pill_' + st).text(lbl);
return pill;
},
/* Connectivity card with a live status pill + per-card refresh button. */
probeCard: function (grid, title, component, cfgRows, probe) {
let card = $$.dc('adm_card', grid);
let hd = $$.dc('adm_card_hd', card);
$$.s(title).appendTo(hd);
let pill = $$.sc('adm_pill').appendTo(hd);
let rbtn = $$.bbtn($adt.refresh, 'adm_btn').appendTo(hd);
let bd = $$.dc('adm_card_bd', card);
$adm.kvTable(bd, cfgRows);
let pf = $$.dc('adm_probe', bd);
let msg = $$.dc('adm_probe_msg', pf);
let met = $$.dc('adm_probe_metrics', pf);
let det = $$.dc('adm_probe_det', pf);
let dur = $$.dc('adm_probe_dur', pf);
let apply = function (p) {
p = p || {};
$adm.setPill(pill, p);
msg.text(p.message || '');
met.empty();
$.each(p.metrics || [], function (i, m) {
let row = $$.dc('adm_metric', met);
$$.sc('adm_metric_k').text(m.label || '').appendTo(row);
$$.sc('adm_metric_v').text(m.value == null ? '' : String(m.value)).appendTo(row);
});
det.text(p.detail || '');
dur.text(p.durationMs != null ? (p.durationMs + ' ms') : '');
};
apply(probe);
rbtn.click(function () {
$adm.setPill(pill, { status: 'checking' });
msg.text(''); met.empty(); det.text(''); dur.text('');
$ocms.postXT({
url: $ocms.url('admin/probe/' + component), success: function (r) {
apply(r.probe);
}, error: function () {
$adm.setPill(pill, { status: 'error' });
msg.text($adt.st_error);
}
});
});
return card;
},
/* ── individual cards ────────────────────────────────────────────────── */
sysCard: function (frm, info) {
let card = $$.dc('adm_card adm_card_wide', frm);
let hd = $$.dc('adm_card_hd', card);
$$.s($adt.host).appendTo(hd);
let env = $$.sc('adm_env').text(info.environment || '');
if (info.isTestDeployment === true) { env.aC('adm_env_test'); }
env.appendTo(hd);
let bd = $$.dc('adm_card_bd', card);
$adm.kvTable(bd, [
[$adt.machine, info.machineName],
[$adt.os, info.osDescription],
[$adt.framework, info.frameworkDescription],
[$adt.env, info.environment],
[$adt.testdep, $adm.yesNo(info.isTestDeployment === true)],
[$adt.pid, info.processId],
[$adt.since, info.processStartUtc ? fdt(info.processStartUtc) : ''],
[$adt.uptime, (info.uptimeHours != null ? (info.uptimeHours + ' ' + $adt.hours) : '')]
]);
},
/* Non-refreshable widget: the one-shot startup self-test result captured at boot. */
startupCard: function (frm, sc) {
let card = $$.dc('adm_card adm_card_wide', frm);
let hd = $$.dc('adm_card_hd', card);
$$.s($adt.startup).appendTo(hd);
let bd = $$.dc('adm_card_bd', card);
if (!sc || sc.ran !== true) {
$adm.setPill($$.sc('adm_pill').appendTo(hd), { status: 'disabled' });
$$.dc('adm_note', bd).text($adt.startup_notrun);
return;
}
let items = sc.items || [];
let anyFail = false;
$.each(items, function (i, it) { if (it.enabled === true && it.ok !== true) { anyFail = true; } });
$adm.setPill($$.sc('adm_pill').appendTo(hd), { status: anyFail ? 'error' : 'ok' });
$adm.kvTable(bd, [
[$adt.startup_on, sc.machineName],
[$adt.startup_at, sc.completedUtc ? fdt(sc.completedUtc) : '']
]);
let list = $$.dc('adm_checks', bd);
$.each(items, function (i, it) {
let row = $$.dc('adm_check', list);
$$.sc('adm_check_k').text($adt['chk_' + it.name] || it.name).appendTo(row);
let stt = it.enabled !== true ? 'skipped' : (it.ok === true ? 'ok' : 'fail');
let lbl = { ok: $adt.chk_ok, fail: $adt.chk_fail, skipped: $adt.chk_skipped }[stt];
$$.sc('adm_check_v adm_check_' + stt).text(lbl).appendTo(row);
});
},
dbCard: function (grid, d, probe) {
$adm.probeCard(grid, $adt.db, 'database', [
[$adt.configured, $adm.yesNo(d.configured === true)],
[$adt.server, d.server],
[$adt.catalog, d.catalog],
[$adt.login, d.userId]
], probe);
},
mailCard: function (grid, e, probe) {
let rows = [
[$adt.mailer, $adm.yesNo(e.mailerEnabled === true)],
[$adt.baseurl, e.baseUrl],
[$adt.account, e.accountId],
[$adt.serverid, e.serverId],
[$adt.token, $adm.yesNo(e.tokenConfigured === true)],
[$adt.overrideActive, $adm.yesNo(e.overrideActive === true)]
];
if (e.overrideActive === true) { rows.push([$adt.override, e.overrideRecipient]); }
// The pill only turns green once the credentials are verified against the Mailer API
// (probe 'email', GET /mailer/fc?fn=verify) — mailerEnabled alone is not proof they work.
let card = $adm.probeCard(grid, $adt.mail, 'email', rows, probe);
let ft = $$.dc('adm_card_ft', card);
$$.bbtn($adt.testmail_btn, 'adm_btn adm_btn_primary').appendTo(ft).click($adm.testMailDlg);
},
blobCard: function (grid, b, probe) {
$adm.probeCard(grid, $adt.blob, 'blob', [
[$adt.enabled, $adm.yesNo(b.enabled === true)],
[$adt.configured, $adm.yesNo(b.configured === true)],
[$adt.invContainer, b.invoiceContainer],
[$adt.remContainer, b.reminderContainer]
], probe);
},
mfrCard: function (grid, m, probe) {
$adm.probeCard(grid, $adt.mfr, 'mfr', [
[$adt.host_l, m.host],
[$adt.creds, $adm.yesNo(m.credentialsConfigured === true)],
[$adt.sync, $adm.yesNo(m.syncEnabled === true)]
], probe);
},
kvCard: function (grid, k, probe) {
$adm.probeCard(grid, $adt.kv, 'keyvault', [
[$adt.vault, k.vaultUri],
[$adt.appname, k.appName],
[$adt.seccount, k.managedSecretCount],
[$adt.clientreg, $adm.yesNo(k.clientRegistered === true)]
], probe);
},
/* ── test email ──────────────────────────────────────────────────────── */
testMailDlg: function () {
$ocms.dlgform([
{ name: 'to', label: $adt.tm_to, type: 'email', required: true, value: ($ocms.auth.email || '') },
{ name: 'subject', label: $adt.tm_subject, type: 'string', required: true, value: $adt.tm_default_subject },
{ name: 'body', label: $adt.tm_body, type: 'text', required: true, value: $adt.tm_default_body }
], {
title: $adt.testmail,
button: $adt.testmail_btn,
size: [420, 560],
addcontent: $$.dc('adm_note').text($adt.tm_hint),
url: $ocms.url('admin/testmail'),
typedvalues: true,
/* Runs inside jQuery's success dispatch — it must never throw, or postXT's
`complete` (which clears the page loading indicator) is skipped. */
success: function (r) {
let res = (r && r.result) || {};
alert(res.message || (res.sent === true ? $adt.tm_sent : $adt.tm_failed));
}
});
}
};
let $$adm = { init2: $adm.init2, auth: {} };
export default $$adm;