ops: live SMS creds on .13; seed-admin cmd in make db-up (admin survives DB resets)

This commit is contained in:
2026-08-10 10:07:29 +10:00
parent e1044f8c32
commit 17150c405d
5 changed files with 101 additions and 3 deletions

View File

@@ -0,0 +1,61 @@
// Command seed-admin creates or refreshes the admin user so it survives DB
// resets (test suites drop the users table). Runs as part of `make db-up`.
// Env: ADMIN_EMAIL (default admin@where-woof.com), ADMIN_PASSWORD (default
// dev-only "AdminPass123!"), ADMIN_NAME (default "Admin").
package main
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgxpool"
"wherewoof/frontend/internal/auth"
"wherewoof/frontend/internal/db"
)
func main() {
dsn := os.Getenv("DATABASE_URL")
if dsn == "" {
fmt.Fprintln(os.Stderr, "error: DATABASE_URL not set")
os.Exit(1)
}
email := os.Getenv("ADMIN_EMAIL")
if email == "" {
email = "admin@where-woof.com"
}
password := os.Getenv("ADMIN_PASSWORD")
if password == "" {
password = "AdminPass123!" // dev default; override in production env
}
name := os.Getenv("ADMIN_NAME")
if name == "" {
name = "Admin"
}
hash, err := auth.HashPassword(password)
if err != nil {
fmt.Fprintln(os.Stderr, "hash:", err)
os.Exit(1)
}
ctx := context.Background()
pool, err := pgxpool.New(ctx, dsn)
if err != nil {
fmt.Fprintln(os.Stderr, "connect:", err)
os.Exit(1)
}
defer pool.Close()
if _, err := db.New(pool).UpsertAdmin(ctx, db.UpsertAdminParams{
Email: email,
PasswordHash: hash,
Name: pgtype.Text{String: name, Valid: name != ""},
}); err != nil {
fmt.Fprintln(os.Stderr, "upsert admin:", err)
os.Exit(1)
}
fmt.Println("admin ready:", email)
}

View File

@@ -36,6 +36,7 @@ type Querier interface {
SetScanPhone(ctx context.Context, arg SetScanPhoneParams) error
SetTagStatus(ctx context.Context, arg SetTagStatusParams) error
UpdateTagDetails(ctx context.Context, arg UpdateTagDetailsParams) (Tag, error)
UpsertAdmin(ctx context.Context, arg UpsertAdminParams) (User, error)
}
var _ Querier = (*Queries)(nil)

View File

@@ -46,6 +46,12 @@ UPDATE tags SET status=$2, updated_at=now() WHERE id=$1;
INSERT INTO tags (tag_code) VALUES ($1)
RETURNING *;
-- name: UpsertAdmin :one
INSERT INTO users (email, password_hash, name, is_admin)
VALUES ($1, $2, $3, true)
ON CONFLICT (email) DO UPDATE SET password_hash = EXCLUDED.password_hash, name = EXCLUDED.name, is_admin = true
RETURNING *;
-- name: InsertScan :one
INSERT INTO scans (tag_id, lat, lng, location_shared, scanner_phone, fingerprint, ip)
VALUES ($1, $2, $3, $4, $5, $6, $7)

View File

@@ -631,3 +631,33 @@ func (q *Queries) UpdateTagDetails(ctx context.Context, arg UpdateTagDetailsPara
)
return i, err
}
const upsertAdmin = `-- name: UpsertAdmin :one
INSERT INTO users (email, password_hash, name, is_admin)
VALUES ($1, $2, $3, true)
ON CONFLICT (email) DO UPDATE SET password_hash = EXCLUDED.password_hash, name = EXCLUDED.name, is_admin = true
RETURNING id, email, password_hash, name, phone, created_at, is_admin, remember_token, paused
`
type UpsertAdminParams struct {
Email string `json:"email"`
PasswordHash string `json:"password_hash"`
Name pgtype.Text `json:"name"`
}
func (q *Queries) UpsertAdmin(ctx context.Context, arg UpsertAdminParams) (User, error) {
row := q.db.QueryRow(ctx, upsertAdmin, arg.Email, arg.PasswordHash, arg.Name)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.PasswordHash,
&i.Name,
&i.Phone,
&i.CreatedAt,
&i.IsAdmin,
&i.RememberToken,
&i.Paused,
)
return i, err
}