mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-01 17:38:15 +00:00
90 lines
3.7 KiB
Go
90 lines
3.7 KiB
Go
package core
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/jleight/meshtender/internal/store"
|
|
"github.com/jleight/meshtender/internal/web"
|
|
)
|
|
|
|
// Ownership transfer hands a repeater to one of its stewards — the co-maintainers
|
|
// the owner already designated. It is the alternative to deleting a node when its
|
|
// owner moves on: the site's documentation, history, and public page address stay
|
|
// with the repeater instead of going away with the account.
|
|
|
|
// transferPath is the transfer page for a repeater, by public id.
|
|
func transferPath(publicID string) string { return "/repeaters/" + publicID + "/transfer" }
|
|
|
|
// transferErr bounces back to the transfer page with an error banner, so the
|
|
// owner can pick a different steward rather than losing the page.
|
|
func transferErr(w http.ResponseWriter, r *http.Request, msg string) {
|
|
web.RedirectErr(w, r, transferPath(repeaterParam(r)), msg)
|
|
}
|
|
|
|
// pageTransferRepeater renders the owner-only recipient picker: which stewards
|
|
// can receive this repeater, and what changes when one does. With no stewards it
|
|
// explains how to designate one instead of offering an empty form.
|
|
//
|
|
// htmx gets the modal fragment (the share page's "Transfer ownership" button);
|
|
// a direct visit — the delete page links here, and a browser that never ran the
|
|
// htmx request lands here too — gets the standalone page. Both render the same
|
|
// content blocks, so there is one copy of the wording.
|
|
func (s *Handlers) pageTransferRepeater(w http.ResponseWriter, r *http.Request) {
|
|
rep, id, ok := s.requireRepeaterOwned(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
stewards, err := s.Store.ListStewards(r.Context(), id)
|
|
if err != nil {
|
|
s.ServerError(w, r, "could not load stewards", err)
|
|
return
|
|
}
|
|
data := map[string]any{
|
|
"Repeater": rep,
|
|
"Nav": web.RepeaterNav(rep.PublicID, rep.Name, rep.OwnerName(), true, "sharing"),
|
|
"Stewards": stewards,
|
|
"Error": r.URL.Query().Get("error"),
|
|
}
|
|
if r.Header.Get("HX-Request") != "" {
|
|
data["Layout"] = "transfer-modal"
|
|
}
|
|
s.Render(w, r, "transfer_repeater.html", data)
|
|
}
|
|
|
|
// handleTransferRepeater hands the repeater to the chosen steward. Authorization
|
|
// is the store's job — it re-checks ownership and the steward flag inside the
|
|
// transaction — so a stale form naming a since-demoted steward is rejected there
|
|
// rather than trusted here.
|
|
func (s *Handlers) handleTransferRepeater(w http.ResponseWriter, r *http.Request) {
|
|
uid := s.Auth.CurrentUserID(r.Context())
|
|
id, ok := s.requireOwned(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
targetID, perr := strconv.ParseInt(r.FormValue("user_id"), 10, 64)
|
|
if perr != nil {
|
|
transferErr(w, r, "Choose who should take over this repeater.")
|
|
return
|
|
}
|
|
err := s.Store.TransferRepeaterToSteward(r.Context(), uid, id, targetID)
|
|
switch {
|
|
case errors.Is(err, store.ErrNotSteward):
|
|
transferErr(w, r, "That person isn't a steward of this repeater anymore. Make them a steward again, or choose someone else.")
|
|
case errors.Is(err, store.ErrDuplicate):
|
|
transferErr(w, r, "That person has already registered this repeater under their own account, so it can't be transferred to them. Ask them to remove their copy first.")
|
|
case errors.Is(err, store.ErrNotFound):
|
|
// Someone else already transferred it, or it was deleted, between the page
|
|
// rendering and this submit. The dashboard is the honest place to land.
|
|
dashErr(w, r, "Could not transfer repeater (only the current owner can).")
|
|
case err != nil:
|
|
s.ServerError(w, r, "could not transfer repeater", err)
|
|
default:
|
|
// The outgoing owner keeps access as a steward, so the repeater page still
|
|
// renders for them — the right place to see the handover took effect.
|
|
web.RedirectFlash(w, r, "/repeaters/"+repeaterParam(r), "ok",
|
|
"Ownership transferred. You're now a steward of this repeater.")
|
|
}
|
|
}
|