docs: record the owner's decisions on routes, compatibility and inbound roles

panel-redesign.md gains four sections. Routes: paths follow the new names
and structure (/overview, /outbound/..., /inbound/..., /server/...,
/account), with one <page>/fragment name, the application form as a
single POST and TestRoutesFollowNavigation. No compatibility: the redesign
is 2.0.0, old paths are removed without redirects, the migration chain is
replaced by one baseline and 2.0 starts from an empty data directory (the
only live installation is the owner's); schema-squash ships as a step of
this plan without its upgrade gate. Who sees what: a domain administrator
manages assigned inbound domains, granted separately from outbound
(user_inbound_domains, per-domain authorization under /inbound/). Room for
roadmap items: where antispam lists, quarantine and preflight land, and
that a feature's screens are drawn mockup-first in this system.

Mockups: the Reload configuration box the first pass missed (now on
Server > Health), the flash component and the sp-help link that replaces
the help drawer, and the user form with two assignment lists. The
antispam plan no longer points its mockup at the superseded panel-ui
folder. Checklist 3/21.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
mix
2026-09-21 22:02:12 +03:00
co-authored by Claude Fable 5.1
parent 1bcfca1586
commit 5e0adb2de5
29 changed files with 293 additions and 78 deletions
@@ -93,7 +93,7 @@
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-info-circle"></i></span><h2>What happens</h2></div>
<div class="sp-box-body">
<p class="mb-2">Changing the password signs out every other session of this account. This one stays signed in.</p>
<p class="sp-muted">A domain administrator sees this same page, and nothing under Server.</p>
<p class="sp-muted">A domain administrator sees this same page, the Outbound and Inbound domains assigned to them, and nothing under Server.</p>
</div>
</div>
</div>
+3 -2
View File
@@ -25,7 +25,7 @@ VOID = {"input", "img", "br", "hr", "meta", "link"}
BULMA = set("""
box button buttons checkbox column columns control field file file-cta file-input file-label
file-name has-addons has-icons-left has-name has-text-danger help icon input label menu menu-label
menu-list progress select table tag textarea title
menu-list notification progress select table tag textarea title
is-3 is-4 is-5 is-6 is-7 is-9 is-active is-danger is-expanded is-fullwidth is-gapless is-half
is-hoverable is-left is-light is-primary is-size-7 is-small is-success is-text is-warning
mb-1 mb-2 mb-3 mb-4 ml-2 mt-4 mt-5
@@ -197,7 +197,8 @@ def main():
if not box.kids or not box.kids[0].has("sp-box-head"):
errors.append(f"{name}: a box must start with box_head")
for k in top[1:]:
ok = k.has("box") or k.has("columns") or (k.tag == "form" and k.has("sp-form"))
# A flash is conditional, so it is allowed at the top level but never part of an outline.
ok = k.has("box") or k.has("columns") or k.has("notification") or (k.tag == "form" and k.has("sp-form"))
if not ok:
errors.append(f"{name}: top-level <{k.tag} class=\"{' '.join(k.classes)}\"> — a page is boxes, columns or one form")
if name != "components":
@@ -72,6 +72,9 @@
<div class="buttons"><a class="button" href="../index.html"><i class="ti ti-arrow-left"></i>Cover sheet</a></div>
</div>
<div class="notification is-success is-light"><i class="ti ti-circle-check"></i><strong>flash</strong> — the result of the last action, between page_head and the first box. Success, or…</div>
<div class="notification is-danger is-light"><i class="ti ti-alert-circle"></i>…<strong>flash, error</strong> — a failure that belongs to the page, not to one field. A field's error stays under the field in Bulma's <code>help is-danger</code>.</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">00</span><h2>Page layouts</h2><div class="sp-end">A page is exactly one of these</div></div>
<div class="sp-box-body">
@@ -115,8 +118,8 @@
<div class="column is-6">
<div class="box">
<div class="sp-box-head"><span class="sp-no">02</span><h2>box · box_head</h2><div class="sp-end">The end slot: a note, a tag, a link or a filter form</div></div>
<div class="sp-box-body"><p>box_body — padded content. A <code>table</code> or a run of <code>sp-record</code> blocks sits directly in the box, without box_body.</p></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>box · box_head</h2><div class="sp-end">The end slot: a note, a tag, a link or a filter form</div><a class="sp-help" href="help.html#checks" title="Optional: the section's Help topic"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-box-body"><p class="mb-2">The <i class="ti ti-help-circle"></i> after the end slot is <code>sp-help</code>: an optional link to the section's topic on the Help page. It replaces today's help drawer.</p><p>box_body — padded content. A <code>table</code> or a run of <code>sp-record</code> blocks sits directly in the box, without box_body.</p></div>
<div class="sp-box-foot"><span>box_foot — a legend, paging</span><span class="sp-end">end slot</span></div>
</div>
<div class="box sp-danger-zone">
@@ -141,6 +141,14 @@
</div>
</div>
<div class="box" id="configuration">
<div class="sp-box-head"><span class="sp-no">06</span><h2>Configuration</h2><div class="sp-end">Safe to run at any time</div></div>
<div class="sp-box-body">
<p class="mb-3">Regenerates the OpenDKIM and Postfix configuration from the database and reloads both daemons. Use it after editing the files by hand, after restoring a backup, or when the running configuration looks out of step with the domain and application lists. It does not touch the queue or the TLS certificate.</p>
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-reload"></i>Reload configuration</button></form>
</div>
</div>
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
</div>
</main>
@@ -96,7 +96,7 @@
<div class="column is-9">
<div class="box" id="dns">
<div class="sp-box-head"><span class="sp-no">01</span><h2>MX record</h2><div class="sp-end">Checked 1 min ago</div></div>
<div class="sp-box-head"><span class="sp-no">01</span><h2>MX record</h2><div class="sp-end">Checked 1 min ago</div><a class="sp-help" href="help.html#inbound" title="Backup-MX and forwarding"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-record">
<div class="sp-record-head"><h3>MX</h3><span class="tag is-success is-light">published</span><small>Keep any existing primary MX if this is a backup-MX</small></div>
<div class="sp-row">
@@ -78,7 +78,7 @@
<div class="control is-expanded has-icons-left"><input class="input" name="name" type="text" placeholder="lists.example.com" autocomplete="off" autocapitalize="none" spellcheck="false" required><span class="icon is-left"><i class="ti ti-world"></i></span></div>
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add domain</button></div>
</div>
<p class="help">Mail is not accepted until an upstream host is saved on the domain's page.</p>
<p class="help">Mail is not accepted until an upstream host is saved on the domain's page. Adding and deleting domains is for a global administrator; a domain administrator sees the list without this form and without Delete.</p>
</form>
</div>
</div>
@@ -108,7 +108,7 @@
<div class="column is-6">
<div class="box" id="limit">
<div class="sp-box-head"><span class="sp-no">02</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div><a class="sp-help" href="help.html#limits" title="Levels 1 and 2"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-box-body">
<form class="sp-form" method="post" action="#">
<div class="field"><label class="label" for="mode">Limit mode</label>
@@ -101,7 +101,7 @@
<div class="column is-9">
<div class="box" id="dns">
<div class="sp-box-head"><span class="sp-no">01</span><h2>DNS records</h2><div class="sp-end">Checked 3 min ago</div></div>
<div class="sp-box-head"><span class="sp-no">01</span><h2>DNS records</h2><div class="sp-end">Checked 3 min ago</div><a class="sp-help" href="help.html#dns" title="What to publish and why"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-record">
<div class="sp-record-head"><h3>DKIM</h3><span class="tag is-success is-light">published</span><small>Not a secret · selector <code>sp2026</code></small></div>
@@ -142,7 +142,7 @@
</div>
<div class="box" id="apps">
<div class="sp-box-head"><span class="sp-no">02</span><h2>Applications</h2><div class="sp-end">SASL logins that may send as this domain</div></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>Applications</h2><div class="sp-end">SASL logins that may send as this domain</div><a class="sp-help" href="help.html#apps" title="What an application is"><i class="ti ti-help-circle"></i></a></div>
<table class="table is-fullwidth is-hoverable">
<thead><tr><th>Login</th><th>May send as</th><th>30 days</th><th>Limits</th><th></th></tr></thead>
<tbody>
@@ -107,7 +107,8 @@
]
}
]
}
},
"box[Configuration]:body"
],
"help": [
"head",
@@ -385,7 +386,8 @@
},
{
"is-6": [
"box[Assigned domains]:body"
"box[Outbound domains]:body",
"box[Inbound domains]:body"
]
}
]
@@ -73,7 +73,7 @@
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>Server health</h2><div class="sp-end"><a href="health.html">All details</a></div></div>
<div class="sp-box-head"><span class="sp-no">01</span><h2>Server health</h2><div class="sp-end"><a href="health.html">All details</a></div><a class="sp-help" href="help.html#checks" title="What these checks mean"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-health">
<a href="health.html#machine"><span class="sp-h-icon"><i class="ti ti-cpu"></i></span><span><span class="sp-h-name">Machine</span><br><span class="sp-h-value">CPU 12 % · RAM 41 %</span><br><span class="sp-h-sub">↓ 18 kB/s · ↑ 44 kB/s</span></span></a>
<a href="health.html#processes"><span class="sp-h-icon"><i class="ti ti-server-cog"></i></span><span><span class="sp-h-name">Processes</span><br><span class="sp-h-value">5 of 5 running</span><br><span class="sp-h-sub">up 18 days</span></span></a>
@@ -30,7 +30,7 @@
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-info-circle"></i></span><h2>What happens</h2></div>
<div class="sp-box-body">
<p class="mb-2">Changing the password signs out every other session of this account. This one stays signed in.</p>
<p class="sp-muted">A domain administrator sees this same page, and nothing under Server.</p>
<p class="sp-muted">A domain administrator sees this same page, the Outbound and Inbound domains assigned to them, and nothing under Server.</p>
</div>
</div>
</div>
@@ -9,6 +9,9 @@
<div class="buttons"><a class="button" href="../index.html"><i class="ti ti-arrow-left"></i>Cover sheet</a></div>
</div>
<div class="notification is-success is-light"><i class="ti ti-circle-check"></i><strong>flash</strong> — the result of the last action, between page_head and the first box. Success, or…</div>
<div class="notification is-danger is-light"><i class="ti ti-alert-circle"></i>…<strong>flash, error</strong> — a failure that belongs to the page, not to one field. A field's error stays under the field in Bulma's <code>help is-danger</code>.</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">00</span><h2>Page layouts</h2><div class="sp-end">A page is exactly one of these</div></div>
<div class="sp-box-body">
@@ -52,8 +55,8 @@
<div class="column is-6">
<div class="box">
<div class="sp-box-head"><span class="sp-no">02</span><h2>box · box_head</h2><div class="sp-end">The end slot: a note, a tag, a link or a filter form</div></div>
<div class="sp-box-body"><p>box_body — padded content. A <code>table</code> or a run of <code>sp-record</code> blocks sits directly in the box, without box_body.</p></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>box · box_head</h2><div class="sp-end">The end slot: a note, a tag, a link or a filter form</div><a class="sp-help" href="help.html#checks" title="Optional: the section's Help topic"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-box-body"><p class="mb-2">The <i class="ti ti-help-circle"></i> after the end slot is <code>sp-help</code>: an optional link to the section's topic on the Help page. It replaces today's help drawer.</p><p>box_body — padded content. A <code>table</code> or a run of <code>sp-record</code> blocks sits directly in the box, without box_body.</p></div>
<div class="sp-box-foot"><span>box_foot — a legend, paging</span><span class="sp-end">end slot</span></div>
</div>
<div class="box sp-danger-zone">
@@ -77,3 +77,11 @@
</div>
</div>
</div>
<div class="box" id="configuration">
<div class="sp-box-head"><span class="sp-no">06</span><h2>Configuration</h2><div class="sp-end">Safe to run at any time</div></div>
<div class="sp-box-body">
<p class="mb-3">Regenerates the OpenDKIM and Postfix configuration from the database and reloads both daemons. Use it after editing the files by hand, after restoring a backup, or when the running configuration looks out of step with the domain and application lists. It does not touch the queue or the TLS certificate.</p>
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-reload"></i>Reload configuration</button></form>
</div>
</div>
@@ -33,7 +33,7 @@
<div class="column is-9">
<div class="box" id="dns">
<div class="sp-box-head"><span class="sp-no">01</span><h2>MX record</h2><div class="sp-end">Checked 1 min ago</div></div>
<div class="sp-box-head"><span class="sp-no">01</span><h2>MX record</h2><div class="sp-end">Checked 1 min ago</div><a class="sp-help" href="help.html#inbound" title="Backup-MX and forwarding"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-record">
<div class="sp-record-head"><h3>MX</h3><span class="tag is-success is-light">published</span><small>Keep any existing primary MX if this is a backup-MX</small></div>
<div class="sp-row">
@@ -15,7 +15,7 @@
<div class="control is-expanded has-icons-left"><input class="input" name="name" type="text" placeholder="lists.example.com" autocomplete="off" autocapitalize="none" spellcheck="false" required><span class="icon is-left"><i class="ti ti-world"></i></span></div>
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add domain</button></div>
</div>
<p class="help">Mail is not accepted until an upstream host is saved on the domain's page.</p>
<p class="help">Mail is not accepted until an upstream host is saved on the domain's page. Adding and deleting domains is for a global administrator; a domain administrator sees the list without this form and without Delete.</p>
</form>
</div>
</div>
@@ -45,7 +45,7 @@
<div class="column is-6">
<div class="box" id="limit">
<div class="sp-box-head"><span class="sp-no">02</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div><a class="sp-help" href="help.html#limits" title="Levels 1 and 2"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-box-body">
<form class="sp-form" method="post" action="#">
<div class="field"><label class="label" for="mode">Limit mode</label>
@@ -38,7 +38,7 @@
<div class="column is-9">
<div class="box" id="dns">
<div class="sp-box-head"><span class="sp-no">01</span><h2>DNS records</h2><div class="sp-end">Checked 3 min ago</div></div>
<div class="sp-box-head"><span class="sp-no">01</span><h2>DNS records</h2><div class="sp-end">Checked 3 min ago</div><a class="sp-help" href="help.html#dns" title="What to publish and why"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-record">
<div class="sp-record-head"><h3>DKIM</h3><span class="tag is-success is-light">published</span><small>Not a secret · selector <code>sp2026</code></small></div>
@@ -79,7 +79,7 @@
</div>
<div class="box" id="apps">
<div class="sp-box-head"><span class="sp-no">02</span><h2>Applications</h2><div class="sp-end">SASL logins that may send as this domain</div></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>Applications</h2><div class="sp-end">SASL logins that may send as this domain</div><a class="sp-help" href="help.html#apps" title="What an application is"><i class="ti ti-help-circle"></i></a></div>
<table class="table is-fullwidth is-hoverable">
<thead><tr><th>Login</th><th>May send as</th><th>30 days</th><th>Limits</th><th></th></tr></thead>
<tbody>
@@ -10,7 +10,7 @@
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>Server health</h2><div class="sp-end"><a href="health.html">All details</a></div></div>
<div class="sp-box-head"><span class="sp-no">01</span><h2>Server health</h2><div class="sp-end"><a href="health.html">All details</a></div><a class="sp-help" href="help.html#checks" title="What these checks mean"><i class="ti ti-help-circle"></i></a></div>
<div class="sp-health">
<a href="health.html#machine"><span class="sp-h-icon"><i class="ti ti-cpu"></i></span><span><span class="sp-h-name">Machine</span><br><span class="sp-h-value">CPU 12 % · RAM 41 %</span><br><span class="sp-h-sub">↓ 18 kB/s · ↑ 44 kB/s</span></span></a>
<a href="health.html#processes"><span class="sp-h-icon"><i class="ti ti-server-cog"></i></span><span><span class="sp-h-name">Processes</span><br><span class="sp-h-value">5 of 5 running</span><br><span class="sp-h-sub">up 18 days</span></span></a>
@@ -1,8 +1,8 @@
<!-- title: Delete shop-team | section: server | page: users -->
<!-- title: Delete acme-ops | section: server | page: users -->
<div class="sp-head">
<div class="sp-grow">
<p class="sp-kicker"><a href="users.html">Users</a> / <a href="user.html">shop-team</a> /</p>
<h1 class="title is-3">Delete shop-team</h1>
<p class="sp-kicker"><a href="users.html">Users</a> / <a href="user.html">acme-ops</a> /</p>
<h1 class="title is-3">Delete acme-ops</h1>
</div>
</div>
@@ -11,15 +11,15 @@
<div class="box sp-danger-zone">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>This cannot be undone</h2></div>
<div class="sp-box-body">
<p class="mb-3">Deleting the panel user <strong>shop-team</strong> will:</p>
<p class="mb-3">Deleting the panel user <strong>acme-ops</strong> will:</p>
<ul class="sp-list-check">
<li><i class="ti ti-x"></i>end any signed-in session of this user at once;</li>
<li><i class="ti ti-x"></i>remove the assignment to <strong>shop.example.org</strong>.</li>
<li><i class="ti ti-x"></i>remove the assignments to <strong>notify.acme.io</strong> (outbound) and <strong>acme.io</strong> (inbound).</li>
</ul>
<p class="sp-muted mb-4">The domain, its applications and their passwords are not affected.</p>
<p class="sp-muted mb-4">The domains, their applications and passwords, upstreams and recipients are not affected.</p>
<form method="post" action="users.html">
<div class="buttons">
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete shop-team</button>
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete acme-ops</button>
<a class="button is-text" href="user.html">Keep it</a>
</div>
</form>
+15 -7
View File
@@ -1,8 +1,8 @@
<!-- title: shop-team · Users | section: server | page: users -->
<!-- title: acme-ops · Users | section: server | page: users -->
<div class="sp-head">
<div class="sp-grow">
<p class="sp-kicker"><a href="users.html">Users</a> /</p>
<h1 class="title is-3">shop-team</h1>
<h1 class="title is-3">acme-ops</h1>
<p class="sp-lead">The same form, empty, creates a new user.</p>
</div>
<a class="button is-danger is-light" href="user-delete.html"><i class="ti ti-trash"></i>Delete user</a>
@@ -14,22 +14,30 @@
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sign-in</h2></div>
<div class="sp-box-body">
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="shop-team" autocomplete="username" spellcheck="false" required></div></div>
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="acme-ops" autocomplete="username" spellcheck="false" required></div></div>
<div class="field"><label class="label" for="password">Password</label><div class="control"><input class="input" id="password" name="password" type="password" autocomplete="new-password"></div>
<p class="help">Leave empty to keep the current one.</p></div>
<div class="field"><label class="label" for="role">Role</label>
<div class="control"><div class="select is-fullwidth"><select id="role" name="role"><option selected>Domain administrator</option><option>Global administrator</option></select></div></div>
<p class="help">A global administrator sees everything, including Inbound and Server. The only global administrator cannot be demoted or deleted.</p></div>
<p class="help">A global administrator sees everything, including Server, and is the only one who adds or deletes domains. The only global administrator cannot be demoted or deleted.</p></div>
</div>
</div>
</div>
<div class="column is-6">
<div class="box">
<div class="sp-box-head"><span class="sp-no">02</span><h2>Assigned domains</h2><div class="sp-end">Required for a domain administrator</div></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound domains</h2><div class="sp-end">DNS records, applications, log</div></div>
<div class="sp-box-body">
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids" checked> shop.example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> notify.acme.io</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> shop.example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids" checked> notify.acme.io</label></div>
</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">03</span><h2>Inbound domains</h2><div class="sp-end">Upstream, recipients, log</div></div>
<div class="sp-box-body">
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids"> lists.example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids" checked> acme.io</label></div>
<p class="help">Granted separately from outbound: the same name on both lists is two assignments. A domain administrator needs at least one domain on either list.</p>
</div>
</div>
</div>
@@ -3,19 +3,21 @@
<div class="sp-grow">
<p class="sp-kicker">Server</p>
<h1 class="title is-3">Users</h1>
<p class="sp-lead">Who may sign in to this panel. A domain administrator sees only the outbound domains assigned to them.</p>
<p class="sp-lead">Who may sign in to this panel. A domain administrator sees only the domains assigned to them — outbound and inbound are granted separately.</p>
</div>
<a class="button is-primary" href="user.html"><i class="ti ti-user-plus"></i>Create user</a>
</div>
<div class="notification is-success is-light"><i class="ti ti-circle-check"></i>User <strong>acme-ops</strong> created.</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>Panel users</h2><div class="sp-end">3 users</div></div>
<table class="table is-fullwidth is-hoverable">
<thead><tr><th>Username</th><th>Role</th><th>Domains</th><th></th></tr></thead>
<thead><tr><th>Username</th><th>Role</th><th>Outbound domains</th><th>Inbound domains</th><th></th></tr></thead>
<tbody>
<tr><td><strong>admin</strong> <span class="tag is-light">you</span></td><td><span class="tag is-primary is-light">global</span></td><td class="sp-muted">All</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>shop-team</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">shop.example.org</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>acme-ops</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">notify.acme.io</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>admin</strong> <span class="tag is-light">you</span></td><td><span class="tag is-primary is-light">global</span></td><td class="sp-muted">All</td><td class="sp-muted">All</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>shop-team</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">shop.example.org</td><td class="sp-muted">—</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>acme-ops</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">notify.acme.io</td><td class="sp-muted">acme.io</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
</tbody>
</table>
</div>
@@ -87,6 +87,14 @@ code { background: var(--sp-well); color: var(--sp-ink); border-radius: 3px; pad
.sp-box-head .sp-no { font-family: var(--sp-mono); font-size: .75rem; font-weight: 600; color: var(--sp-brick); }
.sp-box-head h2 { font-family: var(--sp-mono); font-size: .8rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; margin: 0; }
.sp-box-head .sp-end { margin-left: auto; color: var(--sp-muted); font-size: .85rem; display: flex; gap: .6rem; align-items: center; }
.sp-box-head .sp-help { color: var(--sp-muted); line-height: 1; }
.sp-box-head .sp-help:hover { color: var(--sp-brick); }
.sp-box-head .sp-end + .sp-help { margin-left: 0; }
.sp-box-head h2 + .sp-help { margin-left: auto; }
.notification { border: 1px solid transparent; padding: .8rem 1.25rem; }
.notification.is-success.is-light { border-color: var(--sp-ok-rule); }
.notification.is-danger.is-light { border-color: var(--sp-fail-rule); }
.notification .ti { margin-right: .5rem; }
.box > .sp-box-body { padding: 1.25rem; }
.box > .sp-box-body > :last-child { margin-bottom: 0; }
.box > .table { margin: 0; }
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Delete shop-team · SelfPost</title>
<title>Delete acme-ops · SelfPost</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
<link rel="stylesheet" href="../shared/brand.css">
@@ -64,8 +64,8 @@
<div class="container sp-page">
<div class="sp-head">
<div class="sp-grow">
<p class="sp-kicker"><a href="users.html">Users</a> / <a href="user.html">shop-team</a> /</p>
<h1 class="title is-3">Delete shop-team</h1>
<p class="sp-kicker"><a href="users.html">Users</a> / <a href="user.html">acme-ops</a> /</p>
<h1 class="title is-3">Delete acme-ops</h1>
</div>
</div>
@@ -74,15 +74,15 @@
<div class="box sp-danger-zone">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>This cannot be undone</h2></div>
<div class="sp-box-body">
<p class="mb-3">Deleting the panel user <strong>shop-team</strong> will:</p>
<p class="mb-3">Deleting the panel user <strong>acme-ops</strong> will:</p>
<ul class="sp-list-check">
<li><i class="ti ti-x"></i>end any signed-in session of this user at once;</li>
<li><i class="ti ti-x"></i>remove the assignment to <strong>shop.example.org</strong>.</li>
<li><i class="ti ti-x"></i>remove the assignments to <strong>notify.acme.io</strong> (outbound) and <strong>acme.io</strong> (inbound).</li>
</ul>
<p class="sp-muted mb-4">The domain, its applications and their passwords are not affected.</p>
<p class="sp-muted mb-4">The domains, their applications and passwords, upstreams and recipients are not affected.</p>
<form method="post" action="users.html">
<div class="buttons">
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete shop-team</button>
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete acme-ops</button>
<a class="button is-text" href="user.html">Keep it</a>
</div>
</form>
+15 -7
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>shop-team · Users · SelfPost</title>
<title>acme-ops · Users · SelfPost</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
<link rel="stylesheet" href="../shared/brand.css">
@@ -65,7 +65,7 @@
<div class="sp-head">
<div class="sp-grow">
<p class="sp-kicker"><a href="users.html">Users</a> /</p>
<h1 class="title is-3">shop-team</h1>
<h1 class="title is-3">acme-ops</h1>
<p class="sp-lead">The same form, empty, creates a new user.</p>
</div>
<a class="button is-danger is-light" href="user-delete.html"><i class="ti ti-trash"></i>Delete user</a>
@@ -77,22 +77,30 @@
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sign-in</h2></div>
<div class="sp-box-body">
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="shop-team" autocomplete="username" spellcheck="false" required></div></div>
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="acme-ops" autocomplete="username" spellcheck="false" required></div></div>
<div class="field"><label class="label" for="password">Password</label><div class="control"><input class="input" id="password" name="password" type="password" autocomplete="new-password"></div>
<p class="help">Leave empty to keep the current one.</p></div>
<div class="field"><label class="label" for="role">Role</label>
<div class="control"><div class="select is-fullwidth"><select id="role" name="role"><option selected>Domain administrator</option><option>Global administrator</option></select></div></div>
<p class="help">A global administrator sees everything, including Inbound and Server. The only global administrator cannot be demoted or deleted.</p></div>
<p class="help">A global administrator sees everything, including Server, and is the only one who adds or deletes domains. The only global administrator cannot be demoted or deleted.</p></div>
</div>
</div>
</div>
<div class="column is-6">
<div class="box">
<div class="sp-box-head"><span class="sp-no">02</span><h2>Assigned domains</h2><div class="sp-end">Required for a domain administrator</div></div>
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound domains</h2><div class="sp-end">DNS records, applications, log</div></div>
<div class="sp-box-body">
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids" checked> shop.example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> notify.acme.io</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> shop.example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids" checked> notify.acme.io</label></div>
</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">03</span><h2>Inbound domains</h2><div class="sp-end">Upstream, recipients, log</div></div>
<div class="sp-box-body">
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids"> lists.example.org</label></div>
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids" checked> acme.io</label></div>
<p class="help">Granted separately from outbound: the same name on both lists is two assignments. A domain administrator needs at least one domain on either list.</p>
</div>
</div>
</div>
+7 -5
View File
@@ -66,19 +66,21 @@
<div class="sp-grow">
<p class="sp-kicker">Server</p>
<h1 class="title is-3">Users</h1>
<p class="sp-lead">Who may sign in to this panel. A domain administrator sees only the outbound domains assigned to them.</p>
<p class="sp-lead">Who may sign in to this panel. A domain administrator sees only the domains assigned to them — outbound and inbound are granted separately.</p>
</div>
<a class="button is-primary" href="user.html"><i class="ti ti-user-plus"></i>Create user</a>
</div>
<div class="notification is-success is-light"><i class="ti ti-circle-check"></i>User <strong>acme-ops</strong> created.</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>Panel users</h2><div class="sp-end">3 users</div></div>
<table class="table is-fullwidth is-hoverable">
<thead><tr><th>Username</th><th>Role</th><th>Domains</th><th></th></tr></thead>
<thead><tr><th>Username</th><th>Role</th><th>Outbound domains</th><th>Inbound domains</th><th></th></tr></thead>
<tbody>
<tr><td><strong>admin</strong> <span class="tag is-light">you</span></td><td><span class="tag is-primary is-light">global</span></td><td class="sp-muted">All</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>shop-team</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">shop.example.org</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>acme-ops</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">notify.acme.io</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>admin</strong> <span class="tag is-light">you</span></td><td><span class="tag is-primary is-light">global</span></td><td class="sp-muted">All</td><td class="sp-muted">All</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>shop-team</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">shop.example.org</td><td class="sp-muted">—</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
<tr><td><strong>acme-ops</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">notify.acme.io</td><td class="sp-muted">acme.io</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
</tbody>
</table>
</div>