refactor: centralize avatar URL generation and backfill existing users
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
|||||||
"mal/internal/database"
|
"mal/internal/database"
|
||||||
"mal/internal/db"
|
"mal/internal/db"
|
||||||
"mal/internal/observability"
|
"mal/internal/observability"
|
||||||
|
"mal/internal/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -91,7 +92,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
id := uuid.New().String()
|
id := uuid.New().String()
|
||||||
avatarURL := fmt.Sprintf("https://api.dicebear.com/9.x/dylan/svg?seed=%s", username)
|
avatarURL := users.DefaultAvatarURL(username)
|
||||||
_, err = dbConn.Exec("INSERT INTO user (id, username, password_hash, avatar_url) VALUES (?, ?, ?, ?)", id, username, string(hash), avatarURL)
|
_, err = dbConn.Exec("INSERT INTO user (id, username, password_hash, avatar_url) VALUES (?, ?, ?, ?)", id, username, string(hash), avatarURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
observability.Error("cli_user_create_failed", "cmd/user", "", map[string]any{"username": username}, err)
|
observability.Error("cli_user_create_failed", "cmd/user", "", map[string]any{"username": username}, err)
|
||||||
@@ -117,7 +118,7 @@ func updateAvatars(dbConn *sql.DB) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarURL := fmt.Sprintf("https://api.dicebear.com/9.x/dylan/svg?seed=%s", username)
|
avatarURL := users.DefaultAvatarURL(username)
|
||||||
_, err := dbConn.Exec("UPDATE user SET avatar_url = ? WHERE id = ?", avatarURL, id)
|
_, err := dbConn.Exec("UPDATE user SET avatar_url = ? WHERE id = ?", avatarURL, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
observability.Error("cli_user_avatar_update_failed", "cmd/user", "", map[string]any{"username": username}, err)
|
observability.Error("cli_user_avatar_update_failed", "cmd/user", "", map[string]any{"username": username}, err)
|
||||||
|
|||||||
46
internal/database/fixes/20260528_backfill_avatar_url.go
Normal file
46
internal/database/fixes/20260528_backfill_avatar_url.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package fixes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"mal/internal/users"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(Fix{
|
||||||
|
ID: "20260528_backfill_avatar_url",
|
||||||
|
Apply: func(ctx context.Context, sqlDB *sql.DB) error {
|
||||||
|
rows, err := sqlDB.QueryContext(ctx, `SELECT id, username FROM user WHERE avatar_url = ''`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = rows.Close() }()
|
||||||
|
|
||||||
|
type userRow struct {
|
||||||
|
id string
|
||||||
|
username string
|
||||||
|
}
|
||||||
|
toUpdate := make([]userRow, 0, 64)
|
||||||
|
for rows.Next() {
|
||||||
|
var r userRow
|
||||||
|
if err := rows.Scan(&r.id, &r.username); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toUpdate = append(toUpdate, r)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range toUpdate {
|
||||||
|
avatarURL := users.DefaultAvatarURL(u.username)
|
||||||
|
if _, err := sqlDB.ExecContext(ctx, `UPDATE user SET avatar_url = ? WHERE id = ?`, avatarURL, u.id); err != nil {
|
||||||
|
return fmt.Errorf("update avatar_url for user %s: %w", u.id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
11
internal/users/avatar.go
Normal file
11
internal/users/avatar.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DefaultAvatarURL(username string) string {
|
||||||
|
seed := url.QueryEscape(strings.TrimSpace(username))
|
||||||
|
return "https://api.dicebear.com/9.x/dylan/svg?seed=" + seed
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user