feat: add user avatar with dicebear dylan
This commit is contained in:
@@ -6,3 +6,4 @@ dist
|
|||||||
*.db-wal
|
*.db-wal
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.git
|
.git
|
||||||
|
static
|
||||||
|
|||||||
@@ -14,19 +14,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) != 3 {
|
|
||||||
log.Fatalf("Usage: go run cmd/user/main.go <username> <password>")
|
|
||||||
}
|
|
||||||
|
|
||||||
username := os.Args[1]
|
|
||||||
password := os.Args[2]
|
|
||||||
|
|
||||||
dbConn, err := db.Open(db.GetDBFile())
|
dbConn, err := db.Open(db.GetDBFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to open db: %v", err)
|
log.Fatalf("failed to open db: %v", err)
|
||||||
}
|
}
|
||||||
defer dbConn.Close()
|
defer dbConn.Close()
|
||||||
|
|
||||||
|
if len(os.Args) == 2 && os.Args[1] == "update-avatar" {
|
||||||
|
updateAvatars(dbConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(os.Args) != 3 {
|
||||||
|
log.Fatalf("Usage: go run cmd/user/main.go <username> <password>\n go run cmd/user/main.go update-avatar")
|
||||||
|
}
|
||||||
|
|
||||||
|
username := os.Args[1]
|
||||||
|
password := os.Args[2]
|
||||||
|
|
||||||
var existingID string
|
var existingID string
|
||||||
err = dbConn.QueryRow("SELECT id FROM user WHERE username = ?", username).Scan(&existingID)
|
err = dbConn.QueryRow("SELECT id FROM user WHERE username = ?", username).Scan(&existingID)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
@@ -64,10 +69,40 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
id := uuid.New().String()
|
id := uuid.New().String()
|
||||||
_, err = dbConn.Exec("INSERT INTO user (id, username, password_hash) VALUES (?, ?, ?)", id, username, string(hash))
|
avatarURL := fmt.Sprintf("https://api.dicebear.com/9.x/dylan/svg?seed=%s", username)
|
||||||
|
_, err = dbConn.Exec("INSERT INTO user (id, username, password_hash, avatar_url) VALUES (?, ?, ?, ?)", id, username, string(hash), avatarURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to create user: %v", err)
|
log.Fatalf("failed to create user: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("User '%s' was created successfully!\n", username)
|
fmt.Printf("User '%s' was created successfully!\n", username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateAvatars(dbConn *sql.DB) {
|
||||||
|
rows, err := dbConn.Query("SELECT id, username FROM user")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to fetch users: %v", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for rows.Next() {
|
||||||
|
var id, username string
|
||||||
|
if err := rows.Scan(&id, &username); err != nil {
|
||||||
|
log.Fatalf("failed to scan user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
avatarURL := fmt.Sprintf("https://api.dicebear.com/9.x/dylan/svg?seed=%s", username)
|
||||||
|
_, err := dbConn.Exec("UPDATE user SET avatar_url = ? WHERE id = ?", avatarURL, id)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to update avatar for %s: %v", username, err)
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Fatalf("iteration error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Updated avatars for %d user(s)\n", count)
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,9 +64,10 @@ type Session struct {
|
|||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
PasswordHash string `json:"password_hash"`
|
PasswordHash string `json:"password_hash"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WatchListEntry struct {
|
type WatchListEntry struct {
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ func (q *Queries) GetUpcomingSeasons(ctx context.Context, userID string) ([]GetU
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getUser = `-- name: GetUser :one
|
const getUser = `-- name: GetUser :one
|
||||||
SELECT id, username, password_hash, created_at FROM user WHERE id = ? LIMIT 1
|
SELECT id, username, password_hash, avatar_url, created_at FROM user WHERE id = ? LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetUser(ctx context.Context, id string) (User, error) {
|
func (q *Queries) GetUser(ctx context.Context, id string) (User, error) {
|
||||||
@@ -479,13 +479,14 @@ func (q *Queries) GetUser(ctx context.Context, id string) (User, error) {
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.PasswordHash,
|
&i.PasswordHash,
|
||||||
|
&i.AvatarURL,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserByUsername = `-- name: GetUserByUsername :one
|
const getUserByUsername = `-- name: GetUserByUsername :one
|
||||||
SELECT id, username, password_hash, created_at FROM user WHERE username = ? LIMIT 1
|
SELECT id, username, password_hash, avatar_url, created_at FROM user WHERE username = ? LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) {
|
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) {
|
||||||
@@ -495,6 +496,7 @@ func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User,
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.PasswordHash,
|
&i.PasswordHash,
|
||||||
|
&i.AvatarURL,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
|
|||||||
3
migrations/016_add_avatar_url.sql
Normal file
3
migrations/016_add_avatar_url.sql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE user ADD COLUMN avatar_url TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
UPDATE user SET avatar_url = 'https://api.dicebear.com/9.x/dylan/svg?seed=' || username WHERE avatar_url = '';
|
||||||
@@ -42,9 +42,11 @@
|
|||||||
<div data-trigger class="cursor-pointer">
|
<div data-trigger class="cursor-pointer">
|
||||||
<button class="flex items-center gap-1 rounded-full p-1 transition-colors hover:bg-surface-hover focus:outline-none">
|
<button class="flex items-center gap-1 rounded-full p-1 transition-colors hover:bg-surface-hover focus:outline-none">
|
||||||
{{if .User}}
|
{{if .User}}
|
||||||
<div class="bg-accent flex h-8 w-8 items-center justify-center overflow-hidden rounded-full text-sm font-semibold text-white">
|
<img
|
||||||
{{slice .User.Username 0 1}}
|
src="{{.User.AvatarURL}}"
|
||||||
</div>
|
alt="{{.User.Username}}"
|
||||||
|
class="h-8 w-8 rounded-full object-cover"
|
||||||
|
/>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="bg-accent flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold text-white">
|
<div class="bg-accent flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold text-white">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||||
|
|||||||
Reference in New Issue
Block a user