refactor(sitesvc): rename Org to Instance, refuse an unmigrated database
The signup form's JSON field becomes instance_name, and the pending-signup document field with it. That collection is sitesvc-private and expires after 24 hours, so no migration is needed, but in-flight signups written before the deploy will fail verification.
This commit is contained in:
@@ -5,10 +5,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
type limiter struct {
|
||||
mu sync.Mutex
|
||||
hits map[string]*window
|
||||
@@ -51,8 +47,6 @@ func (l *limiter) allow(key string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (l *limiter) gc(now time.Time) {
|
||||
if now.Sub(l.lastGC) < l.window {
|
||||
return
|
||||
|
||||
@@ -22,10 +22,10 @@ const (
|
||||
)
|
||||
|
||||
type signupBody struct {
|
||||
OrgName string `json:"org_name"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Website string `json:"website"`
|
||||
InstanceName string `json:"instance_name"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Website string `json:"website"`
|
||||
}
|
||||
|
||||
func (s *Server) handleSignup(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -41,7 +41,7 @@ func (s *Server) handleSignup(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var problems []fieldError
|
||||
|
||||
orgName, nameErr := text("org_name", body.OrgName, true, maxShort)
|
||||
instanceName, nameErr := text("instance_name", body.InstanceName, true, maxShort)
|
||||
if nameErr != nil {
|
||||
problems = append(problems, *nameErr)
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func (s *Server) handleSignup(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
token, err := store.CreatePending(ctx, orgName, addr, body.Password)
|
||||
token, err := store.CreatePending(ctx, instanceName, addr, body.Password)
|
||||
switch {
|
||||
case errors.Is(err, store.ErrEmailTaken):
|
||||
writeJSON(w, http.StatusConflict, map[string]any{
|
||||
@@ -95,7 +95,7 @@ func (s *Server) handleSignup(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
case errors.Is(err, store.ErrNameRejected):
|
||||
writeFieldErrors(w, []fieldError{{
|
||||
Field: "org_name",
|
||||
Field: "instance_name",
|
||||
Message: strings.TrimPrefix(err.Error(), store.ErrNameRejected.Error()+": "),
|
||||
}})
|
||||
return
|
||||
@@ -108,7 +108,7 @@ func (s *Server) handleSignup(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
link := s.verifyURL(token)
|
||||
if err := s.mail.SendVerification(addr, orgName, link, store.PendingTTL); err != nil {
|
||||
if err := s.mail.SendVerification(addr, instanceName, link, store.PendingTTL); err != nil {
|
||||
|
||||
log.Printf("signup: send verification to %s: %v", addr, err)
|
||||
writeJSON(w, http.StatusBadGateway, map[string]string{
|
||||
@@ -136,7 +136,7 @@ func (s *Server) handleVerify(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
org, err := store.Verify(ctx, token)
|
||||
inst, err := store.Verify(ctx, token)
|
||||
switch {
|
||||
case errors.Is(err, store.ErrBadToken):
|
||||
s.verifyPage(w, http.StatusGone, "Link expired",
|
||||
@@ -157,23 +157,23 @@ func (s *Server) handleVerify(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("verify: provisioned org %s (%s)", org.Slug, org.OrgID)
|
||||
log.Printf("verify: provisioned instance %s (%s)", inst.Slug, inst.InstanceID)
|
||||
|
||||
if login := s.loginURL(org.Slug); login != "" {
|
||||
if login := s.loginURL(inst.Slug); login != "" {
|
||||
http.Redirect(w, r, login, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
s.verifyPage(w, http.StatusOK, "Organisation ready",
|
||||
fmt.Sprintf("%s is set up and you are its owner. You can sign in now.", org.Name))
|
||||
fmt.Sprintf("%s is set up and you are its owner. You can sign in now.", inst.Name))
|
||||
}
|
||||
|
||||
// loginURL is the org-specific sign-in URL a verified owner is sent to. Each
|
||||
// org lives on its own subdomain (<slug>.vantage.hostxtra.co.uk), so the slug
|
||||
// loginURL is the instance-specific sign-in URL a verified owner is sent to. Each
|
||||
// instance lives on its own subdomain (<slug>.vantage.hostxtra.co.uk), so the slug
|
||||
// must be substituted per signup rather than pointing at one shared address.
|
||||
//
|
||||
// APP_LOGIN_URL is a template. A "{slug}" placeholder is replaced with the
|
||||
// org's slug; a value without one is treated as a literal (a single shared
|
||||
// instance's slug; a value without one is treated as a literal (a single shared
|
||||
// login page) so a plain URL still works. An empty value falls back to the
|
||||
// confirmation page.
|
||||
func (s *Server) loginURL(slug string) string {
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
|
||||
|
||||
var emailRe = regexp.MustCompile(`^[^@\s]+@[^@\s.]+\.[^@\s]+$`)
|
||||
|
||||
const (
|
||||
@@ -23,9 +21,6 @@ type fieldError struct {
|
||||
|
||||
func (e fieldError) Error() string { return e.Field + ": " + e.Message }
|
||||
|
||||
|
||||
|
||||
|
||||
func text(name, value string, required bool, max int) (string, *fieldError) {
|
||||
v := strings.TrimSpace(value)
|
||||
if v == "" {
|
||||
@@ -55,9 +50,6 @@ func email(name, value string) (string, *fieldError) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func oneOf(name, value string, allowed []string) (string, *fieldError) {
|
||||
v := strings.TrimSpace(value)
|
||||
for _, a := range allowed {
|
||||
|
||||
Reference in New Issue
Block a user