refactor(store): single account name on users; rename profile/laps tables

users.display_name becomes users.name and is now the only human-facing
name -- profiles.name is dropped (it duplicated the account name; the
Profile page's Name field now edits users.name through PUT /api/profile,
which carries Name alongside the profiles columns). The profile table
becomes profiles and laps becomes activity_laps, homogeneous with
activity_samples. Onboarding pre-fills the display name from the OIDC
claim's name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 16:11:23 +02:00
parent e2b2bf9611
commit 042579a396
19 changed files with 109 additions and 79 deletions

View File

@@ -11,18 +11,18 @@ import (
// state) is scoped to exactly one User -- see
// docs/superpowers/specs/2026-07-25-per-user-profile-design.md.
type User struct {
ID int64
OIDCSub string
DisplayName string
CreatedAt string
ID int64
OIDCSub string
Name string
CreatedAt string
}
// GetUserBySub looks up a user by their OIDC subject -- the only lookup key
// the session-resolution middleware ever uses.
func (db *DB) GetUserBySub(ctx context.Context, oidcSub string) (User, bool, error) {
var u User
err := db.QueryRowContext(ctx, `SELECT id, oidc_sub, display_name, created_at FROM users WHERE oidc_sub = ?`, oidcSub).
Scan(&u.ID, &u.OIDCSub, &u.DisplayName, &u.CreatedAt)
err := db.QueryRowContext(ctx, `SELECT id, oidc_sub, name, created_at FROM users WHERE oidc_sub = ?`, oidcSub).
Scan(&u.ID, &u.OIDCSub, &u.Name, &u.CreatedAt)
if err == sql.ErrNoRows {
return User{}, false, nil
}
@@ -63,7 +63,7 @@ func (db *DB) ProvisionUser(ctx context.Context, oidcSub, displayName string) (i
}
defer tx.Rollback()
res, err := tx.ExecContext(ctx, `INSERT INTO users (oidc_sub, display_name) VALUES (?, ?)`, oidcSub, displayName)
res, err := tx.ExecContext(ctx, `INSERT INTO users (oidc_sub, name) VALUES (?, ?)`, oidcSub, displayName)
if err != nil {
return 0, fmt.Errorf("create user %q: %w", oidcSub, err)
}
@@ -72,7 +72,7 @@ func (db *DB) ProvisionUser(ctx context.Context, oidcSub, displayName string) (i
return 0, err
}
if _, err := tx.ExecContext(ctx, `INSERT INTO profile (user_id, name) VALUES (?, ?)`, userID, displayName); err != nil {
if _, err := tx.ExecContext(ctx, `INSERT INTO profiles (user_id) VALUES (?)`, userID); err != nil {
return 0, fmt.Errorf("create profile for user %d: %w", userID, err)
}
@@ -113,3 +113,12 @@ func (db *DB) DeleteUser(ctx context.Context, userID int64) error {
}
return nil
}
// UpdateUserName renames userID's account -- the single human-facing name
// (shown in the header and session info), editable from the Profile page.
func (db *DB) UpdateUserName(ctx context.Context, userID int64, name string) error {
if _, err := db.ExecContext(ctx, `UPDATE users SET name = ? WHERE id = ?`, name, userID); err != nil {
return fmt.Errorf("update name for user %d: %w", userID, err)
}
return nil
}