Fix hidden add/remove template buttons, add per-template login filter
The add/remove template buttons sat directly below the fixed-height list with only 6px of margin to the tab's bottom edge - enough on this dev machine but not on the operator's real one (different DPI/font scale), so the buttons rendered off the visible area. Moved them above the list instead (visibility no longer depends on how much space is left at the bottom) and gave the whole "Шаблоны" tab substantially more vertical room throughout. Also added an optional per-template login regex filter (MailTemplateDefinition.LoginPattern): once a file's suffix matches a template, its login must also match that template's pattern (if any) or the row surfaces as a clear Error in the preview instead of silently going through - addresses "непонятно что будет работать". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
804b9610ca
commit
6e823954a3
8 files changed
+385
-50
No files matched your search
@@ -24,6 +24,10 @@ namespace BulkArchiveMailer.Core
|
||||
|
||||
[DataMember(Name = "bodyHtml")]
|
||||
public string BodyHtml { get; set; } = "";
|
||||
|
||||
/// <summary>See MailTemplateDefinition.LoginPattern. Absent from any config.json written before v1.2.0 - DataContractJsonSerializer leaves this null (not "") for those, same caveat as every other string member here; ToTemplateList/SetTemplates and every call site normalize via `?? ""`.</summary>
|
||||
[DataMember(Name = "loginPattern")]
|
||||
public string LoginPattern { get; set; } = "";
|
||||
}
|
||||
|
||||
/// <summary>Shape of %APPDATA%\BulkArchiveMailer\config.json (and of the embedded config.default.json that seeds it).</summary>
|
||||
@@ -95,6 +99,7 @@ namespace BulkArchiveMailer.Core
|
||||
FileSuffix = t?.FileSuffix ?? "",
|
||||
Subject = t?.Subject ?? "",
|
||||
BodyHtml = t?.BodyHtml ?? "",
|
||||
LoginPattern = t?.LoginPattern ?? "",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -115,6 +120,7 @@ namespace BulkArchiveMailer.Core
|
||||
FileSuffix = t?.FileSuffix ?? "",
|
||||
Subject = t?.Subject ?? "",
|
||||
BodyHtml = t?.BodyHtml ?? "",
|
||||
LoginPattern = t?.LoginPattern ?? "",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -380,6 +386,11 @@ namespace BulkArchiveMailer.Core
|
||||
FileSuffix = "ssh_access",
|
||||
Subject = legacy?.Templates?.SshAccess?.Subject ?? "",
|
||||
BodyHtml = legacy?.Templates?.SshAccess?.BodyHtml ?? "",
|
||||
// The legacy shape never had a per-template login
|
||||
// filter - migrated entries keep the old
|
||||
// unrestricted "suffix alone decides the match"
|
||||
// behavior.
|
||||
LoginPattern = "",
|
||||
},
|
||||
new MailTemplateData
|
||||
{
|
||||
@@ -387,6 +398,7 @@ namespace BulkArchiveMailer.Core
|
||||
FileSuffix = "config_update",
|
||||
Subject = legacy?.Templates?.ConfigUpdate?.Subject ?? "",
|
||||
BodyHtml = legacy?.Templates?.ConfigUpdate?.BodyHtml ?? "",
|
||||
LoginPattern = "",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BulkArchiveMailer.Core
|
||||
{
|
||||
@@ -72,6 +73,25 @@ namespace BulkArchiveMailer.Core
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(matchedTemplate.LoginPattern) && !LoginMatchesPattern(parsed.Login, matchedTemplate.LoginPattern))
|
||||
{
|
||||
// Suffix matched, but the operator scoped this template
|
||||
// to a subset of logins (e.g. a per-team/per-suffix
|
||||
// custom template) and this file's login falls outside
|
||||
// it - surfaced next to the other per-file
|
||||
// "recognized but does not qualify" case (suffix
|
||||
// mismatch above) rather than silently picking a
|
||||
// different template or falling through to
|
||||
// byLogin/Ready.
|
||||
result.UnrecognizedFiles.Add(new SendJob
|
||||
{
|
||||
ArchivePath = filePath,
|
||||
Status = SendStatus.Error,
|
||||
ErrorMessage = $"Логин \"{parsed.Login}\" не соответствует фильтру шаблона \"{matchedTemplate.DisplayName}\" ({matchedTemplate.LoginPattern}): {fileName}",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!byLogin.TryGetValue(parsed.Login, out var list))
|
||||
{
|
||||
list = new List<(string, string)>();
|
||||
@@ -151,5 +171,25 @@ namespace BulkArchiveMailer.Core
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether login satisfies the template's LoginPattern regex
|
||||
/// (case-insensitive). An invalid pattern (e.g. hand-edited
|
||||
/// config.json with broken regex syntax) is treated as "does not
|
||||
/// match" rather than throwing and aborting the whole folder scan -
|
||||
/// SettingsForm.btnSave_Click is where a bad pattern is meant to be
|
||||
/// caught, before it ever reaches disk.
|
||||
/// </summary>
|
||||
private static bool LoginMatchesPattern(string login, string pattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Regex.IsMatch(login, pattern, RegexOptions.IgnoreCase);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,5 +30,19 @@ namespace BulkArchiveMailer.Core
|
||||
public string Subject { get; set; } = "";
|
||||
|
||||
public string BodyHtml { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Optional regular expression the parsed login (from
|
||||
/// "{login}_{FileSuffix}.7z") must match (case-insensitively) for
|
||||
/// this template to actually apply, checked by ArchiveScanner AFTER
|
||||
/// the file's suffix has already matched this template. Empty (the
|
||||
/// default) means "no restriction" - the original, pre-v1.2.0
|
||||
/// behavior where suffix alone decides the match. Validated for
|
||||
/// compilability by SettingsForm at save time; ArchiveScanner
|
||||
/// itself treats an uncompilable pattern defensively as "does not
|
||||
/// match" rather than throwing, in case an operator hand-edits
|
||||
/// config.json with a broken regex.
|
||||
/// </summary>
|
||||
public string LoginPattern { get; set; } = "";
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,15 @@
|
||||
"displayName": "Новый доступ (ключ + конфиг)",
|
||||
"fileSuffix": "ssh_access",
|
||||
"subject": "Доступ к SSH jump-хосту — {Login}",
|
||||
"bodyHtml": "<p>Здравствуйте!</p><p>Во вложении — архив с вашим персональным SSH-ключом, готовым клиентским конфигом и краткой инструкцией по подключению (<b>{Login}_ssh_access.7z</b>).</p><p>Пароль от архива будет отправлен отдельным сообщением — не пересылайте архив вместе с паролем.</p><p>Если по какой-то причине не получается распаковать архив или подключиться — напишите в ответ на это письмо.</p>"
|
||||
"bodyHtml": "<p>Здравствуйте!</p><p>Во вложении — архив с вашим персональным SSH-ключом, готовым клиентским конфигом и краткой инструкцией по подключению (<b>{Login}_ssh_access.7z</b>).</p><p>Пароль от архива будет отправлен отдельным сообщением — не пересылайте архив вместе с паролем.</p><p>Если по какой-то причине не получается распаковать архив или подключиться — напишите в ответ на это письмо.</p>",
|
||||
"loginPattern": ""
|
||||
},
|
||||
{
|
||||
"displayName": "Обновление конфига",
|
||||
"fileSuffix": "config_update",
|
||||
"subject": "Обновление конфигурации доступа — {Login}",
|
||||
"bodyHtml": "<p>Здравствуйте!</p><p>Список доступных вам через jump-хост сервисов изменился. Во вложении — обновлённый клиентский конфиг и инструкция (<b>{Login}_config_update.7z</b>). Ваш существующий SSH-ключ по-прежнему действителен, переустанавливать его не нужно — достаточно заменить конфиг.</p><p>Пароль от архива будет отправлен отдельным сообщением — не пересылайте архив вместе с паролем.</p>"
|
||||
"bodyHtml": "<p>Здравствуйте!</p><p>Список доступных вам через jump-хост сервисов изменился. Во вложении — обновлённый клиентский конфиг и инструкция (<b>{Login}_config_update.7z</b>). Ваш существующий SSH-ключ по-прежнему действителен, переустанавливать его не нужно — достаточно заменить конфиг.</p><p>Пароль от архива будет отправлен отдельным сообщением — не пересылайте архив вместе с паролем.</p>",
|
||||
"loginPattern": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user