mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-02 10:03:43 +00:00
156 lines
6.8 KiB
HTML
156 lines
6.8 KiB
HTML
{{define "title"}}Sharing · {{.Repeater.Name}} · MeshTender{{end}}
|
|
{{define "header"}}{{template "repeater-header" .Nav}}{{end}}
|
|
{{define "content"}}
|
|
{{template "repeater-tabs" .Nav}}
|
|
{{if .Error}}<div class="alert alert-danger">{{.Error}}</div>{{end}}
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<p class="text-secondary mb-0">
|
|
Decide who can operate this repeater. Everyone you grant access uses MeshTender's single identity, so
|
|
you never hand out keys — and you can revoke or withdraw at any time.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row row-cards mt-1">
|
|
<div class="col-lg-7">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">Share links</h2>
|
|
<div class="card-actions">
|
|
<button type="button" class="btn btn-primary" data-testid="new-invite"
|
|
data-bs-toggle="modal" data-bs-target="#invite-modal"
|
|
hx-get="/repeaters/{{.Repeater.PublicID}}/share/link/new"
|
|
hx-target="#invite-modal-content" hx-swap="innerHTML">{{template "icon-plus" "me-1"}}Create link</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-secondary mb-0">
|
|
Invite specific people with single-use links: the recipient signs in and accepts to control this
|
|
repeater from their own browser-connected KISS modem. Each link works once and disappears when it's
|
|
used — create one per person, label it, and choose what they can do. Links also
|
|
<strong>expire {{.InviteTTLDays}} days</strong> after you create them, so an unused one can't be
|
|
redeemed by whoever finds it later.
|
|
</p>
|
|
|
|
{{if .Invites}}
|
|
<div class="list-group list-group-flush mt-3">
|
|
{{range .Invites}}
|
|
<div class="list-group-item px-0" data-testid="invite-row">
|
|
<div class="d-flex align-items-center gap-2 mb-1">
|
|
<span class="fw-bold">{{if .Description}}{{.Description}}{{else}}(no label){{end}}</span>
|
|
{{if .Expired}}
|
|
<span class="badge bg-red-lt" data-testid="invite-expired">Expired</span>
|
|
{{else}}
|
|
<span class="badge bg-yellow-lt">Unused</span>
|
|
{{end}}
|
|
</div>
|
|
{{if .Expired}}
|
|
{{/* No copy control for a dead link — handing out a token that can't be
|
|
redeemed just produces a confused recipient. */}}
|
|
<p class="text-secondary mb-2">
|
|
Expired {{ ts .ExpiresAt "datetime" }} and can no longer be accepted. Create a new link instead.
|
|
</p>
|
|
{{else}}
|
|
<div class="input-group mb-2">
|
|
<code class="form-control font-monospace" id="link-{{.ID}}">{{$.BaseURL}}/invite/{{.Token}}</code>
|
|
<button type="button" class="btn" data-copy-target="#link-{{.ID}}">{{template "icon-copy" ""}}Copy</button>
|
|
</div>
|
|
<p class="text-secondary small mb-2">Expires {{ ts .ExpiresAt "datetime" }}</p>
|
|
{{end}}
|
|
<form method="post" action="/repeaters/{{$.Repeater.PublicID}}/share/link/delete" class="m-0">
|
|
<input type="hidden" name="invite_id" value="{{.ID}}">
|
|
<button type="submit" class="btn btn-sm btn-ghost-danger">
|
|
{{if .Expired}}Remove{{else}}Revoke link{{end}}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<p class="text-secondary mt-3 mb-0">No pending links.</p>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<h2 class="card-title">People with access</h2>
|
|
<div class="card-actions">
|
|
<button type="button" class="btn" data-testid="transfer-ownership"
|
|
data-bs-toggle="modal" data-bs-target="#transfer-modal"
|
|
hx-get="/repeaters/{{.Repeater.PublicID}}/transfer"
|
|
hx-target="#transfer-modal-content" hx-swap="innerHTML">Transfer ownership</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-secondary">
|
|
Mark a trusted person a <strong>steward</strong> to make them a co-operator: they can run
|
|
<strong>every</strong> command (including risky ones), just like you, and they're listed as a
|
|
backup maintainer on this repeater's public page — so the network knows who can service it if
|
|
you can't. Everyone else only gets the specific commands you grant them.
|
|
</p>
|
|
{{if .Shares}}
|
|
<div class="list-group list-group-flush">
|
|
{{range .Shares}}
|
|
<div class="list-group-item d-flex align-items-center gap-2 flex-wrap px-0" data-testid="share-person-row">
|
|
<span class="fw-bold">{{.Name}}</span>
|
|
<span class="text-secondary">@{{.Username}}</span>
|
|
{{if .Steward}}<span class="badge bg-azure-lt" title="Co-operator: can run every command, including risky ones">Steward · full access</span>{{end}}
|
|
<button type="button" class="btn btn-sm ms-auto" data-bs-toggle="modal" data-bs-target="#person-modal"
|
|
data-testid="manage-person"
|
|
hx-get="/repeaters/{{$.Repeater.PublicID}}/share/{{.UserID}}/access"
|
|
hx-target="#person-modal-content" hx-swap="innerHTML">Manage access</button>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<p class="text-secondary">No one has accepted yet.</p>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-5">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h2 class="card-title">Organizations</h2>
|
|
<p class="text-secondary">
|
|
This repeater is shared with every organization you belong to, so their admins and members can run the
|
|
commands those organizations are permitted to run on it. Use <strong>Manage access</strong> to opt it
|
|
out of an organization or limit which commands that organization may run on <em>this</em> repeater.
|
|
</p>
|
|
|
|
{{if .Orgs}}
|
|
{{template "org-access-rows" .}}
|
|
{{else}}
|
|
<p class="text-secondary">Join an organization to share this repeater with it.</p>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Shared modals; the "Create single-use link", per-person "Manage access",
|
|
and "Transfer ownership" buttons load their fragments here via htmx. -->
|
|
<div class="modal modal-blur fade" id="invite-modal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg" role="document">
|
|
<div class="modal-content" id="invite-modal-content"></div>
|
|
</div>
|
|
</div>
|
|
<div class="modal modal-blur fade" id="person-modal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg" role="document">
|
|
<div class="modal-content" id="person-modal-content"></div>
|
|
</div>
|
|
</div>
|
|
<div class="modal modal-blur fade" id="transfer-modal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
|
|
<div class="modal-content" id="transfer-modal-content"></div>
|
|
</div>
|
|
</div>
|
|
{{template "limits-modal-shell" .}}
|
|
|
|
<a class="back-link mt-3" href="/">{{template "icon-arrow-left" "me-1"}}Back to dashboard</a>
|
|
{{end}}
|