mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 21:34:38 +03:00
add ability to include custom css & js in web interface (webmail, webaccount, webadmin), and use css variables in webmail for easier customization
if files {webmail,webaccount,webadmin}.{css,js} exist in the configdir (where the mox.conf file lives), their contents are included in the web apps. the webmail now uses css variables, mostly for colors. so you can write a custom webmail.css that changes the variables, e.g.: :root { --color: blue } you can also look at css class names and override their styles. in the future, we may want to make some css variables configurable in the per-user settings in the webmail. should reduce the number of variables first. any custom javascript is loaded first. if it defines a global function "moxBeforeDisplay", that is called each time a page loads (after authentication) with the DOM element of the page content as parameter. the webmail is a single persistent page. this can be used to make some changes to the DOM, e.g. inserting some elements. we'll have to see how well this works in practice. perhaps some patterns emerge (e.g. adding a logo), and we can make those use-cases easier to achieve. helps partially with issue #114, and based on questions from laura-lilly on matrix.
This commit is contained in:
@ -5,6 +5,8 @@ declare let page: HTMLElement
|
||||
declare let moxversion: string
|
||||
declare let moxgoos: string
|
||||
declare let moxgoarch: string
|
||||
// From customization script.
|
||||
declare let moxBeforeDisplay: (webmailroot: HTMLElement) => void
|
||||
|
||||
const login = async (reason: string) => {
|
||||
return new Promise<string>((resolve: (v: string) => void, _) => {
|
||||
@ -346,7 +348,7 @@ const index = async () => {
|
||||
let recvID: HTMLInputElement
|
||||
let cidElem: HTMLSpanElement
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs('Mox Admin'),
|
||||
checkUpdatesEnabled ? [] : dom.p(box(yellow, 'Warning: Checking for updates has not been enabled in mox.conf (CheckUpdates: true).', dom.br(), 'Make sure you stay up to date through another mechanism!', dom.br(), 'You have a responsibility to keep the internet-connected software you run up to date and secure!', dom.br(), 'See ', link('https://updates.xmox.nl/changelog'))),
|
||||
dom.p(
|
||||
@ -439,7 +441,7 @@ const globalRoutes = async () => {
|
||||
client.Config(),
|
||||
])
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Routes',
|
||||
@ -451,7 +453,7 @@ const globalRoutes = async () => {
|
||||
const config = async () => {
|
||||
const [staticPath, dynamicPath, staticText, dynamicText] = await client.ConfigFiles()
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Config',
|
||||
@ -473,7 +475,7 @@ const loglevels = async () => {
|
||||
let pkg: HTMLInputElement
|
||||
let level: HTMLSelectElement
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Log levels',
|
||||
@ -584,7 +586,7 @@ const accounts = async () => {
|
||||
let account: HTMLInputElement
|
||||
let accountModified = false
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Accounts',
|
||||
@ -803,7 +805,7 @@ const account = async (name: string) => {
|
||||
return v*mult
|
||||
}
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Accounts', '#accounts'),
|
||||
@ -1219,7 +1221,7 @@ const domain = async (d: string) => {
|
||||
)
|
||||
}
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Domain ' + domainString(dnsdomain),
|
||||
@ -1794,7 +1796,7 @@ const domainAlias = async (d: string, aliasLocalpart: string) => {
|
||||
|
||||
let delFieldset: HTMLFieldSetElement
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Domain ' + domainString(domain.Domain), '#domains/'+d),
|
||||
@ -1901,7 +1903,7 @@ const domainDNSRecords = async (d: string) => {
|
||||
client.ParseDomain(d),
|
||||
])
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Domain ' + domainString(dnsdomain), '#domains/'+d),
|
||||
@ -2056,7 +2058,7 @@ const domainDNSCheck = async (d: string) => {
|
||||
),
|
||||
]
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Domain ' + domainString(dnsdomain), '#domains/'+d),
|
||||
@ -2082,7 +2084,7 @@ const domainDNSCheck = async (d: string) => {
|
||||
}
|
||||
|
||||
const dmarcIndex = async () => {
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'DMARC',
|
||||
@ -2103,7 +2105,7 @@ const dmarcReports = async () => {
|
||||
const start = new Date(new Date().getTime() - 30*24*3600*1000)
|
||||
const summaries = await client.DMARCSummaries(start, end, "")
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('DMARC', '#dmarc'),
|
||||
@ -2165,7 +2167,7 @@ const dmarcEvaluations = async () => {
|
||||
|
||||
const nextmonth = new Date(new Date().getTime()+31*24*3600*1000)
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('DMARC', '#dmarc'),
|
||||
@ -2305,7 +2307,7 @@ const dmarcEvaluationsDomain = async (domain: string) => {
|
||||
return r
|
||||
}
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('DMARC', '#dmarc'),
|
||||
@ -2411,7 +2413,7 @@ const domainDMARC = async (d: string) => {
|
||||
|
||||
// todo future: table sorting? period selection (last day, 7 days, 1 month, 1 year, custom period)? collapse rows for a report? show totals per report? a simple bar graph to visualize messages and dmarc/dkim/spf fails? similar for TLSRPT.
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Domain ' + domainString(dnsdomain), '#domains/'+d),
|
||||
@ -2578,7 +2580,7 @@ const domainDMARCReport = async (d: string, reportID: number) => {
|
||||
client.Domain(d),
|
||||
])
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Domain ' + domainString(dnsdomain), '#domains/'+d),
|
||||
@ -2591,7 +2593,7 @@ const domainDMARCReport = async (d: string, reportID: number) => {
|
||||
}
|
||||
|
||||
const tlsrptIndex = async () => {
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'TLSRPT',
|
||||
@ -2621,7 +2623,7 @@ const tlsrptResults = async () => {
|
||||
let comment: HTMLInputElement
|
||||
const nextmonth = new Date(new Date().getTime()+31*24*3600*1000)
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('TLSRPT', '#tlsrpt'),
|
||||
@ -2758,7 +2760,7 @@ const tlsrptResultsPolicyDomain = async (isrcptdom: boolean, domain: string) =>
|
||||
const recordPromise = client.LookupTLSRPTRecord(domain)
|
||||
|
||||
let recordBox: HTMLElement
|
||||
dom._kids(page,
|
||||
const root = dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('TLSRPT', '#tlsrpt'),
|
||||
@ -2808,6 +2810,8 @@ const tlsrptResultsPolicyDomain = async (isrcptdom: boolean, domain: string) =>
|
||||
}
|
||||
dom._kids(recordBox, l)
|
||||
})()
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
const tlsrptReports = async () => {
|
||||
@ -2815,7 +2819,7 @@ const tlsrptReports = async () => {
|
||||
const start = new Date(new Date().getTime() - 30*24*3600*1000)
|
||||
const summaries = await client.TLSRPTSummaries(start, end, '')
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('TLSRPT', '#tlsrpt'),
|
||||
@ -2872,7 +2876,7 @@ const domainTLSRPT = async (d: string) => {
|
||||
return s
|
||||
}
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('TLSRPT', '#tlsrpt'),
|
||||
@ -2968,7 +2972,7 @@ const domainTLSRPTID = async (d: string, reportID: number) => {
|
||||
client.ParseDomain(d),
|
||||
])
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('TLSRPT', '#tlsrpt'),
|
||||
@ -2984,7 +2988,7 @@ const domainTLSRPTID = async (d: string, reportID: number) => {
|
||||
const mtasts = async () => {
|
||||
const policies = await client.MTASTSPolicies()
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'MTA-STS policies',
|
||||
@ -3056,7 +3060,7 @@ const dnsbl = async () => {
|
||||
let fieldset: HTMLFieldSetElement
|
||||
let monitorTextarea: HTMLTextAreaElement
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'DNS blocklist status for IPs',
|
||||
@ -3256,7 +3260,7 @@ const queueList = async () => {
|
||||
window.location.reload() // todo: reload less
|
||||
})
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Queue',
|
||||
@ -3696,7 +3700,7 @@ const retiredList = async () => {
|
||||
}
|
||||
render()
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Queue', '#queue'),
|
||||
@ -3978,7 +3982,7 @@ const hooksList = async () => {
|
||||
window.location.reload() // todo: reload less
|
||||
})
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Webhook queue',
|
||||
@ -4266,7 +4270,7 @@ const hooksRetiredList = async () => {
|
||||
}
|
||||
render()
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
crumblink('Webhook queue', '#webhookqueue'),
|
||||
@ -5039,7 +5043,7 @@ const webserver = async () => {
|
||||
]
|
||||
}
|
||||
|
||||
dom._kids(page,
|
||||
return dom.div(
|
||||
crumbs(
|
||||
crumblink('Mox Admin', '#'),
|
||||
'Webserver config',
|
||||
@ -5121,67 +5125,72 @@ const init = async () => {
|
||||
const t = h.split('/')
|
||||
page.classList.add('loading')
|
||||
try {
|
||||
let root: HTMLElement
|
||||
if (h == '') {
|
||||
await index()
|
||||
root = await index()
|
||||
} else if (h === 'config') {
|
||||
await config()
|
||||
root = await config()
|
||||
} else if (h === 'loglevels') {
|
||||
await loglevels()
|
||||
root = await loglevels()
|
||||
} else if (h === 'accounts') {
|
||||
await accounts()
|
||||
root = await accounts()
|
||||
} else if (t[0] === 'accounts' && t.length === 2) {
|
||||
await account(t[1])
|
||||
root = await account(t[1])
|
||||
} else if (t[0] === 'domains' && t.length === 2) {
|
||||
await domain(t[1])
|
||||
root = await domain(t[1])
|
||||
} else if (t[0] === 'domains' && t.length === 4 && t[2] === 'alias') {
|
||||
await domainAlias(t[1], t[3])
|
||||
root = await domainAlias(t[1], t[3])
|
||||
} else if (t[0] === 'domains' && t.length === 3 && t[2] === 'dmarc') {
|
||||
await domainDMARC(t[1])
|
||||
root = await domainDMARC(t[1])
|
||||
} else if (t[0] === 'domains' && t.length === 4 && t[2] === 'dmarc' && parseInt(t[3])) {
|
||||
await domainDMARCReport(t[1], parseInt(t[3]))
|
||||
root = await domainDMARCReport(t[1], parseInt(t[3]))
|
||||
} else if (t[0] === 'domains' && t.length === 3 && t[2] === 'dnscheck') {
|
||||
await domainDNSCheck(t[1])
|
||||
root = await domainDNSCheck(t[1])
|
||||
} else if (t[0] === 'domains' && t.length === 3 && t[2] === 'dnsrecords') {
|
||||
await domainDNSRecords(t[1])
|
||||
root = await domainDNSRecords(t[1])
|
||||
} else if (h === 'queue') {
|
||||
await queueList()
|
||||
root = await queueList()
|
||||
} else if (h === 'queue/retired') {
|
||||
await retiredList()
|
||||
root = await retiredList()
|
||||
} else if (h === 'webhookqueue') {
|
||||
await hooksList()
|
||||
root = await hooksList()
|
||||
} else if (h === 'webhookqueue/retired') {
|
||||
await hooksRetiredList()
|
||||
root = await hooksRetiredList()
|
||||
} else if (h === 'tlsrpt') {
|
||||
await tlsrptIndex()
|
||||
root = await tlsrptIndex()
|
||||
} else if (h === 'tlsrpt/reports') {
|
||||
await tlsrptReports()
|
||||
root = await tlsrptReports()
|
||||
} else if (t[0] === 'tlsrpt' && t[1] === 'reports' && t.length === 3) {
|
||||
await domainTLSRPT(t[2])
|
||||
root = await domainTLSRPT(t[2])
|
||||
} else if (t[0] === 'tlsrpt' && t[1] === 'reports' && t.length === 4 && parseInt(t[3])) {
|
||||
await domainTLSRPTID(t[2], parseInt(t[3]))
|
||||
root = await domainTLSRPTID(t[2], parseInt(t[3]))
|
||||
} else if (h === 'tlsrpt/results') {
|
||||
await tlsrptResults()
|
||||
root = await tlsrptResults()
|
||||
} else if (t[0] == 'tlsrpt' && t[1] == 'results' && (t[2] === 'rcptdom' || t[2] == 'host') && t.length === 4) {
|
||||
await tlsrptResultsPolicyDomain(t[2] === 'rcptdom', t[3])
|
||||
root = await tlsrptResultsPolicyDomain(t[2] === 'rcptdom', t[3])
|
||||
} else if (h === 'dmarc') {
|
||||
await dmarcIndex()
|
||||
root = await dmarcIndex()
|
||||
} else if (h === 'dmarc/reports') {
|
||||
await dmarcReports()
|
||||
root = await dmarcReports()
|
||||
} else if (h === 'dmarc/evaluations') {
|
||||
await dmarcEvaluations()
|
||||
root = await dmarcEvaluations()
|
||||
} else if (t[0] == 'dmarc' && t[1] == 'evaluations' && t.length === 3) {
|
||||
await dmarcEvaluationsDomain(t[2])
|
||||
root = await dmarcEvaluationsDomain(t[2])
|
||||
} else if (h === 'mtasts') {
|
||||
await mtasts()
|
||||
root = await mtasts()
|
||||
} else if (h === 'dnsbl') {
|
||||
await dnsbl()
|
||||
root = await dnsbl()
|
||||
} else if (h === 'routes') {
|
||||
await globalRoutes()
|
||||
root = await globalRoutes()
|
||||
} else if (h === 'webserver') {
|
||||
await webserver()
|
||||
root = await webserver()
|
||||
} else {
|
||||
dom._kids(page, 'page not found')
|
||||
root = dom.div('page not found')
|
||||
}
|
||||
if ((window as any).moxBeforeDisplay) {
|
||||
moxBeforeDisplay(root)
|
||||
}
|
||||
dom._kids(page, root)
|
||||
} catch (err) {
|
||||
console.log('error', err)
|
||||
window.alert('Error: ' + errmsg(err))
|
||||
|
Reference in New Issue
Block a user