webadmin: consistent pattern for client api calls wrapped in async/await

adding await in the closure. makes no functional different. but let's stick to one form.
This commit is contained in:
Mechiel Lukkien 2024-05-09 11:31:04 +02:00
parent 98ce133203
commit 76aa96ab6f
No known key found for this signature in database
2 changed files with 12 additions and 12 deletions

View file

@ -2274,7 +2274,7 @@ const account = async (name) => {
}))))), dom.br(), dom.h2('Settings'), dom.form(fieldsetSettings = dom.fieldset(dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Maximum outgoing messages per day', attr.title('Maximum number of outgoing messages for this account in a 24 hour window. This limits the damage to recipients and the reputation of this mail server in case of account compromise. Default 1000. MaxOutgoingMessagesPerDay in configuration file.')), dom.br(), maxOutgoingMessagesPerDay = dom.input(attr.type('number'), attr.required(''), attr.value('' + (config.MaxOutgoingMessagesPerDay || 1000)))), dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Maximum first-time recipients per day', attr.title('Maximum number of first-time recipients in outgoing messages for this account in a 24 hour window. This limits the damage to recipients and the reputation of this mail server in case of account compromise. Default 200. MaxFirstTimeRecipientsPerDay in configuration file.')), dom.br(), maxFirstTimeRecipientsPerDay = dom.input(attr.type('number'), attr.required(''), attr.value('' + (config.MaxFirstTimeRecipientsPerDay || 200)))), dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Disk usage quota: Maximum total message size ', attr.title('Default maximum total message size in bytes for the account, overriding any globally configured default maximum size if non-zero. A negative value can be used to have no limit in case there is a limit by default. Attempting to add new messages to an account beyond its maximum total size will result in an error. Useful to prevent a single account from filling storage. Use units "k" for kilobytes, or "m", "g", "t".')), dom.br(), quotaMessageSize = dom.input(attr.value(formatQuotaSize(config.QuotaMessageSize))), ' Current usage is ', formatQuotaSize(Math.floor(diskUsage / (1024 * 1024)) * 1024 * 1024), '.'), dom.div(style({ display: 'block', marginBottom: '.5ex' }), dom.label(firstTimeSenderDelay = dom.input(attr.type('checkbox'), config.NoFirstTimeSenderDelay ? [] : attr.checked('')), ' ', dom.span('Delay deliveries from first-time senders.', attr.title('To slow down potential spammers, when the message is misclassified as non-junk. Turning off the delay can be useful when the account processes messages automatically and needs fast responses.')))), dom.submitbutton('Save')), async function submit(e) { }))))), dom.br(), dom.h2('Settings'), dom.form(fieldsetSettings = dom.fieldset(dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Maximum outgoing messages per day', attr.title('Maximum number of outgoing messages for this account in a 24 hour window. This limits the damage to recipients and the reputation of this mail server in case of account compromise. Default 1000. MaxOutgoingMessagesPerDay in configuration file.')), dom.br(), maxOutgoingMessagesPerDay = dom.input(attr.type('number'), attr.required(''), attr.value('' + (config.MaxOutgoingMessagesPerDay || 1000)))), dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Maximum first-time recipients per day', attr.title('Maximum number of first-time recipients in outgoing messages for this account in a 24 hour window. This limits the damage to recipients and the reputation of this mail server in case of account compromise. Default 200. MaxFirstTimeRecipientsPerDay in configuration file.')), dom.br(), maxFirstTimeRecipientsPerDay = dom.input(attr.type('number'), attr.required(''), attr.value('' + (config.MaxFirstTimeRecipientsPerDay || 200)))), dom.label(style({ display: 'block', marginBottom: '.5ex' }), dom.span('Disk usage quota: Maximum total message size ', attr.title('Default maximum total message size in bytes for the account, overriding any globally configured default maximum size if non-zero. A negative value can be used to have no limit in case there is a limit by default. Attempting to add new messages to an account beyond its maximum total size will result in an error. Useful to prevent a single account from filling storage. Use units "k" for kilobytes, or "m", "g", "t".')), dom.br(), quotaMessageSize = dom.input(attr.value(formatQuotaSize(config.QuotaMessageSize))), ' Current usage is ', formatQuotaSize(Math.floor(diskUsage / (1024 * 1024)) * 1024 * 1024), '.'), dom.div(style({ display: 'block', marginBottom: '.5ex' }), dom.label(firstTimeSenderDelay = dom.input(attr.type('checkbox'), config.NoFirstTimeSenderDelay ? [] : attr.checked('')), ' ', dom.span('Delay deliveries from first-time senders.', attr.title('To slow down potential spammers, when the message is misclassified as non-junk. Turning off the delay can be useful when the account processes messages automatically and needs fast responses.')))), dom.submitbutton('Save')), async function submit(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
await check(fieldsetSettings, (async () => client.AccountSettingsSave(name, parseInt(maxOutgoingMessagesPerDay.value) || 0, parseInt(maxFirstTimeRecipientsPerDay.value) || 0, xparseSize(quotaMessageSize.value), firstTimeSenderDelay.checked))()); await check(fieldsetSettings, (async () => await client.AccountSettingsSave(name, parseInt(maxOutgoingMessagesPerDay.value) || 0, parseInt(maxFirstTimeRecipientsPerDay.value) || 0, xparseSize(quotaMessageSize.value), firstTimeSenderDelay.checked))());
}), dom.br(), dom.h2('Set new password'), formPassword = dom.form(fieldsetPassword = dom.fieldset(dom.label(style({ display: 'inline-block' }), 'New password', dom.br(), password = dom.input(attr.type('password'), attr.autocomplete('new-password'), attr.required(''), function focus() { }), dom.br(), dom.h2('Set new password'), formPassword = dom.form(fieldsetPassword = dom.fieldset(dom.label(style({ display: 'inline-block' }), 'New password', dom.br(), password = dom.input(attr.type('password'), attr.autocomplete('new-password'), attr.required(''), function focus() {
passwordHint.style.display = ''; passwordHint.style.display = '';
})), ' ', dom.submitbutton('Change password')), passwordHint = dom.div(style({ display: 'none', marginTop: '.5ex' }), dom.clickbutton('Generate random password', function click(e) { })), ' ', dom.submitbutton('Change password')), passwordHint = dom.div(style({ display: 'none', marginTop: '.5ex' }), dom.clickbutton('Generate random password', function click(e) {
@ -2436,7 +2436,7 @@ const domain = async (d) => {
if (!window.confirm('Are you sure? A key will be generated by the server, the selector configured but disabled. The page will reload, so unsaved changes to other DKIM selectors will be lost. After adding the key, first add the selector to DNS, then enable it for signing outgoing messages.')) { if (!window.confirm('Are you sure? A key will be generated by the server, the selector configured but disabled. The page will reload, so unsaved changes to other DKIM selectors will be lost. After adding the key, first add the selector to DNS, then enable it for signing outgoing messages.')) {
return; return;
} }
await check(fieldset, (async () => client.DomainDKIMAdd(d, selector.value, algorithm.value, hash.value, canonHeader.value === 'relaxed', canonBody.value === 'relaxed', seal.checked, headers.value.split('\n').map(s => s.trim()).filter(s => s), parseDuration(lifetime.value)))()); await check(fieldset, (async () => await client.DomainDKIMAdd(d, selector.value, algorithm.value, hash.value, canonHeader.value === 'relaxed', canonBody.value === 'relaxed', seal.checked, headers.value.split('\n').map(s => s.trim()).filter(s => s), parseDuration(lifetime.value)))());
window.alert("Selector added. Page will be reloaded. Don't forget to add the selector to DNS, see suggested DNS records, and don't forget to enable the selector afterwards."); window.alert("Selector added. Page will be reloaded. Don't forget to add the selector to DNS, see suggested DNS records, and don't forget to enable the selector afterwards.");
window.location.reload(); // todo: reload only dkim section window.location.reload(); // todo: reload only dkim section
}, fieldset = dom.fieldset(dom.div(style({ display: 'flex', gap: '1em' }), dom.div(dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Selector', attr.title('Used in the DKIM-Signature header, and used to form a DNS record under ._domainkey.<domain>.'), dom.div(selector = dom.input(attr.required(''), attr.value(defaultSelector())))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Algorithm', attr.title('For signing messages. RSA is common at the time of writing, not all mail servers recognize ed25519 signature.'), dom.div(algorithm = dom.select(dom.option('rsa'), dom.option('ed25519')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Hash', attr.title("Used in signing messages. Don't use sha1 unless you understand the consequences."), dom.div(hash = dom.select(dom.option('sha256')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Canonicalization - header', attr.title('Canonicalization processes the message headers before signing. Relaxed allows more whitespace changes, making it more likely for DKIM signatures to validate after transit through servers that make whitespace modifications. Simple is more strict.'), dom.div(canonHeader = dom.select(dom.option('relaxed'), dom.option('simple')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Canonicalization - body', attr.title('Like canonicalization for headers, but for the bodies.'), dom.div(canonBody = dom.select(dom.option('relaxed'), dom.option('simple')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Signature lifetime', attr.title('How long a signature remains valid. Should be as long as a message may take to be delivered. The signature must be valid at the time a message is being delivered to the final destination.'), dom.div(lifetime = dom.input(attr.value('3d'), attr.required('')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Seal headers', attr.title("DKIM-signatures cover headers. If headers are not sealed, additional message headers can be added with the same key without invalidating the signature. This may confuse software about which headers are trustworthy. Sealing is the safer option."), dom.div(seal = dom.input(attr.type('checkbox'), attr.checked(''))))), dom.div(dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Headers (optional)', attr.title('Headers to sign. If left empty, a set of standard headers are signed. The (standard set of) headers are most easily edited after creating the selector/key.'), dom.div(headers = dom.textarea(attr.rows('15')))))), dom.div(dom.submitbutton('Add'))))); }, fieldset = dom.fieldset(dom.div(style({ display: 'flex', gap: '1em' }), dom.div(dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Selector', attr.title('Used in the DKIM-Signature header, and used to form a DNS record under ._domainkey.<domain>.'), dom.div(selector = dom.input(attr.required(''), attr.value(defaultSelector())))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Algorithm', attr.title('For signing messages. RSA is common at the time of writing, not all mail servers recognize ed25519 signature.'), dom.div(algorithm = dom.select(dom.option('rsa'), dom.option('ed25519')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Hash', attr.title("Used in signing messages. Don't use sha1 unless you understand the consequences."), dom.div(hash = dom.select(dom.option('sha256')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Canonicalization - header', attr.title('Canonicalization processes the message headers before signing. Relaxed allows more whitespace changes, making it more likely for DKIM signatures to validate after transit through servers that make whitespace modifications. Simple is more strict.'), dom.div(canonHeader = dom.select(dom.option('relaxed'), dom.option('simple')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Canonicalization - body', attr.title('Like canonicalization for headers, but for the bodies.'), dom.div(canonBody = dom.select(dom.option('relaxed'), dom.option('simple')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Signature lifetime', attr.title('How long a signature remains valid. Should be as long as a message may take to be delivered. The signature must be valid at the time a message is being delivered to the final destination.'), dom.div(lifetime = dom.input(attr.value('3d'), attr.required('')))), dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Seal headers', attr.title("DKIM-signatures cover headers. If headers are not sealed, additional message headers can be added with the same key without invalidating the signature. This may confuse software about which headers are trustworthy. Sealing is the safer option."), dom.div(seal = dom.input(attr.type('checkbox'), attr.checked(''))))), dom.div(dom.label(style({ display: 'block', marginBottom: '1ex' }), 'Headers (optional)', attr.title('Headers to sign. If left empty, a set of standard headers are signed. The (standard set of) headers are most easily edited after creating the selector/key.'), dom.div(headers = dom.textarea(attr.rows('15')))))), dom.div(dom.submitbutton('Add')))));
@ -3324,12 +3324,12 @@ const queueList = async () => {
render(); render();
const buttonNextAttemptSet = (text, minutes) => dom.clickbutton(text, async function click(e) { const buttonNextAttemptSet = (text, minutes) => dom.clickbutton(text, async function click(e) {
// note: awkward client call because gatherIDs() can throw an exception. // note: awkward client call because gatherIDs() can throw an exception.
const n = await check(e.target, (async () => client.QueueNextAttemptSet(gatherIDs(), minutes))()); const n = await check(e.target, (async () => await client.QueueNextAttemptSet(gatherIDs(), minutes))());
window.alert('' + n + ' message(s) updated'); window.alert('' + n + ' message(s) updated');
window.location.reload(); // todo: reload less window.location.reload(); // todo: reload less
}); });
const buttonNextAttemptAdd = (text, minutes) => dom.clickbutton(text, async function click(e) { const buttonNextAttemptAdd = (text, minutes) => dom.clickbutton(text, async function click(e) {
const n = await check(e.target, (async () => client.QueueNextAttemptAdd(gatherIDs(), minutes))()); const n = await check(e.target, (async () => await client.QueueNextAttemptAdd(gatherIDs(), minutes))());
window.alert('' + n + ' message(s) updated'); window.alert('' + n + ' message(s) updated');
window.location.reload(); // todo: reload less window.location.reload(); // todo: reload less
}); });
@ -3625,12 +3625,12 @@ const hooksList = async () => {
render(); render();
const buttonNextAttemptSet = (text, minutes) => dom.clickbutton(text, async function click(e) { const buttonNextAttemptSet = (text, minutes) => dom.clickbutton(text, async function click(e) {
// note: awkward client call because gatherIDs() can throw an exception. // note: awkward client call because gatherIDs() can throw an exception.
const n = await check(e.target, (async () => client.HookNextAttemptSet(gatherIDs(), minutes))()); const n = await check(e.target, (async () => await client.HookNextAttemptSet(gatherIDs(), minutes))());
window.alert('' + n + ' hook(s) updated'); window.alert('' + n + ' hook(s) updated');
window.location.reload(); // todo: reload less window.location.reload(); // todo: reload less
}); });
const buttonNextAttemptAdd = (text, minutes) => dom.clickbutton(text, async function click(e) { const buttonNextAttemptAdd = (text, minutes) => dom.clickbutton(text, async function click(e) {
const n = await check(e.target, (async () => client.HookNextAttemptAdd(gatherIDs(), minutes))()); const n = await check(e.target, (async () => await client.HookNextAttemptAdd(gatherIDs(), minutes))());
window.alert('' + n + ' hook(s) updated'); window.alert('' + n + ' hook(s) updated');
window.location.reload(); // todo: reload less window.location.reload(); // todo: reload less
}); });

View file

@ -945,7 +945,7 @@ const account = async (name: string) => {
async function submit(e: SubmitEvent) { async function submit(e: SubmitEvent) {
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()
await check(fieldsetSettings, (async () => client.AccountSettingsSave(name, parseInt(maxOutgoingMessagesPerDay.value) || 0, parseInt(maxFirstTimeRecipientsPerDay.value) || 0, xparseSize(quotaMessageSize.value), firstTimeSenderDelay.checked))()) await check(fieldsetSettings, (async () => await client.AccountSettingsSave(name, parseInt(maxOutgoingMessagesPerDay.value) || 0, parseInt(maxFirstTimeRecipientsPerDay.value) || 0, xparseSize(quotaMessageSize.value), firstTimeSenderDelay.checked))())
}, },
), ),
dom.br(), dom.br(),
@ -1151,7 +1151,7 @@ const domain = async (d: string) => {
if (!window.confirm('Are you sure? A key will be generated by the server, the selector configured but disabled. The page will reload, so unsaved changes to other DKIM selectors will be lost. After adding the key, first add the selector to DNS, then enable it for signing outgoing messages.')) { if (!window.confirm('Are you sure? A key will be generated by the server, the selector configured but disabled. The page will reload, so unsaved changes to other DKIM selectors will be lost. After adding the key, first add the selector to DNS, then enable it for signing outgoing messages.')) {
return return
} }
await check(fieldset, (async () => client.DomainDKIMAdd(d, selector.value, algorithm.value, hash.value, canonHeader.value === 'relaxed', canonBody.value === 'relaxed', seal.checked, headers.value.split('\n').map(s => s.trim()).filter(s => s), parseDuration(lifetime.value)))()) await check(fieldset, (async () => await client.DomainDKIMAdd(d, selector.value, algorithm.value, hash.value, canonHeader.value === 'relaxed', canonBody.value === 'relaxed', seal.checked, headers.value.split('\n').map(s => s.trim()).filter(s => s), parseDuration(lifetime.value)))())
window.alert("Selector added. Page will be reloaded. Don't forget to add the selector to DNS, see suggested DNS records, and don't forget to enable the selector afterwards.") window.alert("Selector added. Page will be reloaded. Don't forget to add the selector to DNS, see suggested DNS records, and don't forget to enable the selector afterwards.")
window.location.reload() // todo: reload only dkim section window.location.reload() // todo: reload only dkim section
}, },
@ -3244,12 +3244,12 @@ const queueList = async () => {
const buttonNextAttemptSet = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) { const buttonNextAttemptSet = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) {
// note: awkward client call because gatherIDs() can throw an exception. // note: awkward client call because gatherIDs() can throw an exception.
const n = await check(e.target! as HTMLButtonElement, (async () => client.QueueNextAttemptSet(gatherIDs(), minutes))()) const n = await check(e.target! as HTMLButtonElement, (async () => await client.QueueNextAttemptSet(gatherIDs(), minutes))())
window.alert(''+n+' message(s) updated') window.alert(''+n+' message(s) updated')
window.location.reload() // todo: reload less window.location.reload() // todo: reload less
}) })
const buttonNextAttemptAdd = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) { const buttonNextAttemptAdd = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) {
const n = await check(e.target! as HTMLButtonElement, (async () => client.QueueNextAttemptAdd(gatherIDs(), minutes))()) const n = await check(e.target! as HTMLButtonElement, (async () => await client.QueueNextAttemptAdd(gatherIDs(), minutes))())
window.alert(''+n+' message(s) updated') window.alert(''+n+' message(s) updated')
window.location.reload() // todo: reload less window.location.reload() // todo: reload less
}) })
@ -3966,12 +3966,12 @@ const hooksList = async () => {
const buttonNextAttemptSet = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) { const buttonNextAttemptSet = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) {
// note: awkward client call because gatherIDs() can throw an exception. // note: awkward client call because gatherIDs() can throw an exception.
const n = await check(e.target! as HTMLButtonElement, (async () => client.HookNextAttemptSet(gatherIDs(), minutes))()) const n = await check(e.target! as HTMLButtonElement, (async () => await client.HookNextAttemptSet(gatherIDs(), minutes))())
window.alert(''+n+' hook(s) updated') window.alert(''+n+' hook(s) updated')
window.location.reload() // todo: reload less window.location.reload() // todo: reload less
}) })
const buttonNextAttemptAdd = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) { const buttonNextAttemptAdd = (text: string, minutes: number) => dom.clickbutton(text, async function click(e: MouseEvent) {
const n = await check(e.target! as HTMLButtonElement, (async () => client.HookNextAttemptAdd(gatherIDs(), minutes))()) const n = await check(e.target! as HTMLButtonElement, (async () => await client.HookNextAttemptAdd(gatherIDs(), minutes))())
window.alert(''+n+' hook(s) updated') window.alert(''+n+' hook(s) updated')
window.location.reload() // todo: reload less window.location.reload() // todo: reload less
}) })