Slug-based RSS URLs and success flash messages
Build and Deploy / build-and-deploy (push) Successful in 45s

- Add podcast_slug to users with global uniqueness check
- RSS available at /rss/{slug} (path-based) with legacy ?slug= fallback
- Slug field in dashboard settings with validation (lowercase, hyphens)
- JS flash messages for success/error after form submissions
- Remove unused imports
This commit is contained in:
2026-07-08 09:29:00 -04:00
parent d70ed1943f
commit b179f57628
7 changed files with 111 additions and 29 deletions
+31 -7
View File
@@ -12,6 +12,7 @@ type User struct {
Email string
PasswordHash string
PodcastTitle string
PodcastSlug string
PodcastImage string
PodcastAuthor string
CreatedAt time.Time
@@ -53,6 +54,7 @@ func migrate(db *sql.DB) error {
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
podcast_title TEXT NOT NULL DEFAULT '',
podcast_slug TEXT NOT NULL DEFAULT '',
podcast_image TEXT NOT NULL DEFAULT '',
podcast_author TEXT NOT NULL DEFAULT '',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
@@ -95,8 +97,8 @@ func (s *Store) CreateUser(email, passwordHash string) (int64, error) {
func (s *Store) UserByEmail(email string) (*User, error) {
u := &User{}
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_image, podcast_author, created_at FROM users WHERE email = ?", email).
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastImage, &u.PodcastAuthor, &u.CreatedAt)
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_slug, podcast_image, podcast_author, created_at FROM users WHERE email = ?", email).
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastSlug, &u.PodcastImage, &u.PodcastAuthor, &u.CreatedAt)
if err != nil {
return nil, err
}
@@ -105,17 +107,39 @@ func (s *Store) UserByEmail(email string) (*User, error) {
func (s *Store) GetUser(id int64) (*User, error) {
u := &User{}
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_image, podcast_author, created_at FROM users WHERE id = ?", id).
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastImage, &u.PodcastAuthor, &u.CreatedAt)
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_slug, podcast_image, podcast_author, created_at FROM users WHERE id = ?", id).
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastSlug, &u.PodcastImage, &u.PodcastAuthor, &u.CreatedAt)
if err != nil {
return nil, err
}
return u, nil
}
func (s *Store) UpdateUserPodcast(id int64, title, image, author string) error {
_, err := s.db.Exec("UPDATE users SET podcast_title=?, podcast_image=?, podcast_author=? WHERE id=?",
title, image, author, id)
func (s *Store) UserBySlug(slug string) (*User, error) {
u := &User{}
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_slug, podcast_image, podcast_author, created_at FROM users WHERE podcast_slug = ?", slug).
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastSlug, &u.PodcastImage, &u.PodcastAuthor, &u.CreatedAt)
if err != nil {
return nil, err
}
return u, nil
}
func (s *Store) SlugTaken(slug string, excludeUserID int64) (bool, error) {
var id int64
err := s.db.QueryRow("SELECT id FROM users WHERE podcast_slug = ? AND id != ?", slug, excludeUserID).Scan(&id)
if err == sql.ErrNoRows {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}
func (s *Store) UpdateUserPodcast(id int64, title, slug, image, author string) error {
_, err := s.db.Exec("UPDATE users SET podcast_title=?, podcast_slug=?, podcast_image=?, podcast_author=? WHERE id=?",
title, slug, image, author, id)
return err
}