mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 14:27:27 +00:00
bbs.profullstack.com launch kit: provisioner, custom domains, email verify, ascii-live
- setup.sh: idempotent one-shot droplet provisioner — agentbbs on :22 (admin OpenSSH moved to :2202), rootless podman, Caddy front end for https://bbs.profullstack.com with tilde-style /~user homepages - internal/sites + domain@ SSH route: self-service custom domains (ssh domain@host add example.com) backed by a symlink farm and an on-demand-TLS ask endpoint so Caddy only issues certs for mapped hosts - internal/mail + join@ email verification: optional email at signup, confirmation link served by a loopback /verify endpoint behind Caddy - internal/source + cmd/ascii-live: live video → terminal ASCII groundwork (docs/ascii-live.md) - store: additive sqlite migrations (email/verify columns, domains table) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9b0f465946
commit
f3f8e70996
14 changed files with 2005 additions and 27 deletions
68
internal/store/store_email_test.go
Normal file
68
internal/store/store_email_test.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmailVerificationRoundtrip(t *testing.T) {
|
||||
st, err := Open(filepath.Join(t.TempDir(), "t.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("open: %v", err)
|
||||
}
|
||||
defer st.Close()
|
||||
|
||||
u, err := st.EnsureUser("alice", "member", "SHA256:aaa")
|
||||
if err != nil {
|
||||
t.Fatalf("ensure: %v", err)
|
||||
}
|
||||
if u.EmailVerified {
|
||||
t.Fatal("new user should be unverified")
|
||||
}
|
||||
|
||||
if err := st.SetEmailVerification(u.ID, "alice@example.com", "tok123"); err != nil {
|
||||
t.Fatalf("set: %v", err)
|
||||
}
|
||||
// Email is stored but not yet verified.
|
||||
got, _, err := st.UserByFingerprint("SHA256:aaa")
|
||||
if err != nil {
|
||||
t.Fatalf("byfp: %v", err)
|
||||
}
|
||||
if got.Email != "alice@example.com" || got.EmailVerified {
|
||||
t.Fatalf("pre-verify state wrong: %+v", got)
|
||||
}
|
||||
|
||||
// Wrong token does nothing.
|
||||
if _, ok, _ := st.VerifyEmail("nope"); ok {
|
||||
t.Fatal("bad token should not verify")
|
||||
}
|
||||
|
||||
// Correct token verifies and returns the account.
|
||||
vu, ok, err := st.VerifyEmail("tok123")
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("verify: ok=%v err=%v", ok, err)
|
||||
}
|
||||
if vu.Name != "alice" || !vu.EmailVerified {
|
||||
t.Fatalf("verify returned wrong user: %+v", vu)
|
||||
}
|
||||
|
||||
// Token is single-use (cleared after verification).
|
||||
if _, ok, _ := st.VerifyEmail("tok123"); ok {
|
||||
t.Fatal("token should be consumed after first use")
|
||||
}
|
||||
final, _, _ := st.UserByName("alice")
|
||||
if !final.EmailVerified {
|
||||
t.Fatal("user should remain verified")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyEmailEmptyToken(t *testing.T) {
|
||||
st, err := Open(filepath.Join(t.TempDir(), "t.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("open: %v", err)
|
||||
}
|
||||
defer st.Close()
|
||||
if _, ok, err := st.VerifyEmail(""); ok || err != nil {
|
||||
t.Fatalf("empty token must be a clean miss: ok=%v err=%v", ok, err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue