Files
podstalk/docs/plans/2026-08-19-allow-signup-env.md
jbrechtel d4d552b3f4
Build and Deploy / build-and-deploy (push) Successful in 1m44s
Fix sign-in page to show Sign Up link only when sign-up is enabled
2026-08-19 10:22:09 -04:00

5.4 KiB
Raw Permalink Blame History

Env-Var-Controlled Sign-Up Implementation Plan

Goal: Gate sign-up and the sign-in "Sign Up" link on the PODSTALK_ALLOW_SIGNUP environment variable instead of the number of existing users.

Architecture: Add an AllowSignup bool field to the AuthHandler. main.go reads PODSTALK_ALLOW_SIGNUP and sets it on the handler. The signup handler and the sign-in page link both consult this boolean. store.UserCount() stays as a general utility but is no longer used by authentication.

Tech Stack: Go (net/http), strconv.ParseBool for env parsing.


File Structure

  • Modify: main.go — read env var, set AllowSignup on the AuthHandler.
  • Modify: handler/auth.go — replace count-based gating with the AllowSignup flag.
  • No changes to store/store.go (UserCount is left in place).
  • Modify: template/signin.html — render the "Sign Up" nav link with {{if .AllowSignup}}.

Task 1: Add AllowSignup field and switch signup gating

Files:

  • Modify: handler/auth.go

  • Step 1: Add the AllowSignup field to AuthHandler and replace the count-based redirect

Replace:

type AuthHandler struct {
	Store    *store.Store
	Sessions *SessionStore
	Tpl      *Templates
}

func (h *AuthHandler) ServeSignup(w http.ResponseWriter, r *http.Request) {
	count, err := h.Store.UserCount()
	if err == nil && count > 0 {
		http.Redirect(w, r, "/signin", http.StatusSeeOther)
		return
	}

with:

type AuthHandler struct {
	Store       *store.Store
	Sessions    *SessionStore
	Tpl         *Templates
	AllowSignup bool
}

func (h *AuthHandler) ServeSignup(w http.ResponseWriter, r *http.Request) {
	if !h.AllowSignup {
		http.Redirect(w, r, "/signin", http.StatusSeeOther)
		return
	}
  • Step 2: Replace the hasUsers() helper with an allowSignup() helper

Replace:

func (h *AuthHandler) hasUsers() bool {
	count, err := h.Store.UserCount()
	return err == nil && count > 0
}

with:

func (h *AuthHandler) allowSignup() bool {
	return h.AllowSignup
}
  • Step 3: Update the three AllowSignup template renders to use allowSignup()

Replace every occurrence of "AllowSignup": h.allowSignup() where it was passed as the HasUser key. In the three render calls in the sign-in handler, ensure the map key is AllowSignup:

map[string]any{"AllowSignup": h.allowSignup()}
map[string]any{"Error": "Invalid email or password.", "AllowSignup": h.allowSignup()}
map[string]any{"Error": "Invalid email or password.", "AllowSignup": h.allowSignup()}
  • Step 3b: Update the sign-in template to use AllowSignup without inversion

In template/signin.html, the nav-link conditional must render the link when sign-up is allowed. Replace {{if not .HasUser}} with {{if .AllowSignup}}. (Retain the {{end}}.)

  • Step 4: Commit
git add handler/auth.go
git commit -m "Gate sign-up on AllowSignup field instead of user count"

Task 2: Read the env var in main.go and wire it up

Files:

  • Modify: main.go

  • Step 1: Add strconv to imports

In main.go, add "strconv" to the import block (after "os").

  • Step 2: Parse PODSTALK_ALLOW_SIGNUP and set it on the handler

After the existing baseURL block, add:

	allowSignup := false
	if v := os.Getenv("PODSTALK_ALLOW_SIGNUP"); v != "" {
		allowSignup = envBool(v)
	}

Change the authH literal to include the flag:

	authH := &handler.AuthHandler{Store: st, Sessions: sessions, Tpl: tpl, AllowSignup: allowSignup}

At the bottom (near envOrDefault), add the envBool helper:

// envBool interprets a string as a truthy boolean.
func envBool(v string) bool {
	if b, err := strconv.ParseBool(v); err == nil {
		return b
	}
	switch strings.ToLower(strings.TrimSpace(v)) {
	case "yes", "on":
		return true
	}
	return false
}

Add "strings" to the imports if not already present (it is not present in main.go; add it).

  • Step 3: Commit
git add main.go
git commit -m "Read PODSTALK_ALLOW_SIGNUP env var to control sign-up"

Task 3: Verify

  • Step 1: Run the build script per AGENTS.md

Run: ./scripts/build Expected: PASS (gofmt, go vet, go build, docker build all succeed).

  • Step 2: Manual sanity check (sandbox permitting)

Run: PODSTALK_ALLOW_SIGNUP=true go run . & then curl -s -o /dev/null -w "%{http_code}\n" -X GET localhost:8080/signup → expect a 200 render. Run: PODSTALK_ALLOW_SIGNUP=false go run . & then curl -s -o /dev/null -w "%{http_code}\n" -X GET localhost:8080/signup → expect 303 redirect to /signin.

  • Step 3: Final commit (if the build produced no fixes) and push
git status
git push

Self-Review

1. Spec coverage:

  • Env var PODSTALK_ALLOW_SIGNUP parsed in main.go → Task 2 Step 2. ✓
  • Default (unset) = disabled → Task 2 Step 2 (allowSignup := false, only overridden when set non-empty). ✓
  • Truthy values (true, 1, yes, on) enabled → ParseBool covers 1/true, the switch covers yes/on. ✓
  • Signup handler redirects when disabled → Task 1 Step 1. ✓
  • Sign-in "Sign Up" link driven by same flag → Task 1 Steps 23 (HasUser). ✓
  • store.UserCount() unchanged → noted in File Structure. ✓

2. Placeholder scan: No TBD/TODO; every task has concrete code. ✓

3. Type consistency: AllowSignup field, allowSignup() method, and envBool helper are named consistently throughout. ✓