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

190 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```go
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:
```go
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:
```go
func (h *AuthHandler) hasUsers() bool {
count, err := h.Store.UserCount()
return err == nil && count > 0
}
```
with:
```go
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`:
```go
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**
```bash
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:
```go
allowSignup := false
if v := os.Getenv("PODSTALK_ALLOW_SIGNUP"); v != "" {
allowSignup = envBool(v)
}
```
Change the `authH` literal to include the flag:
```go
authH := &handler.AuthHandler{Store: st, Sessions: sessions, Tpl: tpl, AllowSignup: allowSignup}
```
At the bottom (near `envOrDefault`), add the `envBool` helper:
```go
// 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**
```bash
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**
```bash
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. ✓