Say every Administration refusal in the reader's language
Stalwart explains a refused change in English, and several of its words reached the page as they were: "Invalid domain name" for a reserved TLD, "Invalid email address" for a catch-all, a grant refusal, and ihasmail's own proxy messages. Every registry error type now has its own message, and a value one of the registry's string validators refused is recognised by the validator's wording and explained again. A domain clash or a missing domain is worded for a domain rather than an account. The one exception is kept on purpose: a password policy's reason follows a translated sentence, because the rule is the server's and dropping it would leave no way to find out why. The mock now refuses a reserved TLD and a catch-all without a domain the way the live server did. KNOWN-ISSUES records the fix, and that the last two live cases -- an administrator-set password and the outranking guard -- held. 15 new strings in all nine catalogues, 3 retired; strings falling back to English stay at 16.
This commit is contained in:
@@ -341,7 +341,9 @@ export function createDirectory(opts: Options) {
|
||||
demand("sysDomainCreate");
|
||||
const o = raw as Obj;
|
||||
const name = String(o.name ?? "");
|
||||
if (!/^([a-z0-9-]+\.)+[a-z0-9-]{2,}$/.test(name)) { notCreated[cid] = setError("invalidProperties", "Invalid domain name.", ["name"]); continue; }
|
||||
// Live on 2026-09-13: a reserved TLD is refused by the registry's
|
||||
// domain validator, as invalidPatch with the validator's own words.
|
||||
if (!/^([a-z0-9-]+\.)+[a-z0-9-]{2,}$/.test(name) || /\.(example|test|invalid|localhost)$/.test(name)) { notCreated[cid] = setError("invalidPatch", "Invalid domain name", ["name"]); continue; }
|
||||
if (taken(name)) { notCreated[cid] = setError("primaryKeyViolation", "A domain with this name already exists.", ["name"]); continue; }
|
||||
const id = `d${counter++}`;
|
||||
domains.push(domain(id, name, { ...o, id, createdAt: new Date().toISOString().replace(/\.\d{3}Z$/, "Z") }));
|
||||
@@ -355,6 +357,8 @@ export function createDirectory(opts: Options) {
|
||||
if (!target) { notUpdated[id] = setError("notFound", "Domain not found."); continue; }
|
||||
const next = structuredClone(target);
|
||||
for (const [path, value] of Object.entries(raw as Obj)) setPointer(next, path, value);
|
||||
// Live on 2026-09-13: a catch-all that is not a whole address.
|
||||
if (typeof next.catchAllAddress === "string" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(next.catchAllAddress)) { notUpdated[id] = setError("invalidPatch", "Invalid email address", ["catchAllAddress"]); continue; }
|
||||
const clash = Object.keys((next.aliases as Obj) ?? {}).find((alias) => alias === next.name || taken(alias, id));
|
||||
if (clash) { notUpdated[id] = setError("primaryKeyViolation", `The name ${clash} is already in use.`, ["aliases"]); continue; }
|
||||
Object.assign(target, next);
|
||||
|
||||
Reference in New Issue
Block a user